Sunday, July 5, 2026

MCP

 A Multiagent Control Plane aka MCP and the ModelContextProtocol from Anthropic als aka MCP, represent two complementary but fundamentally different ways of organizing intelligence inside complex autonomous systems. In drone video sensing pipelines such as a dvsaapi architecture, both patterns appear naturally because the system must coordinate multiple decisionmaking components while also ensuring that each model invocation is grounded in the correct operational context. Although they often coexist, they solve different problems: the control plane governs *how* agents collaborate to achieve a mission, while the protocol governs *how* each model call is structured, contextualized, and made reliable.

A multiagent control plane is a supervisory fabric that orchestrates autonomous components. It defines roles, responsibilities, communication rules, and escalation paths among agents that may specialize in perception, planning, navigation, anomaly detection, or missionlevel reasoning. In a drone video sensing workflow, one agent may handle framelevel semantic segmentation, another may track objects across time, another may evaluate inflection signatures in trajectories, and another may decide whether the drone should adjust altitude or camera angle. The control plane ensures that these agents do not behave like isolated microservices but instead operate as a coordinated team with shared state, shared goals, and shared constraints. It manages arbitration when agents disagree, prioritizes safetycritical signals, and enforces missionlevel policies such as geofencing, batteryaware routing, or privacypreserving video capture. In practice, this means the control plane is responsible for agent lifecycle management, concurrency, delegation, and the routing of tasks to the right agent at the right time. It is the backbone that allows agentic UAV systems to scale from simple singledrone missions to complex multidrone fleets performing collaborative sensing.

The ModelContextProtocol is about ensuring that each model invocation is predictable, auditable, and grounded. It defines how a model is called, what context accompanies the call, and how the output is interpreted. In drone video sensing, this matters because perception models are extremely sensitive to the surrounding metadata: camera intrinsics, GPS coordinates, IMU readings, timestamps, mission parameters, and environmental conditions. A model that receives raw pixels without context may misclassify a vehicle, misjudge depth, or fail to detect an inflection point in a trajectory. The protocol ensures that every model call includes the correct context bundle, that the model’s output is wrapped in a structured schema, and that downstream agents can reliably consume the result. It also enforces versioning, grounding rules, and safety constraints so that the system never invokes a model with stale calibration data or missing geospatial metadata. In short, the protocol governs the *contract* between the model and the rest of the system.

Realworld UAV usecases can explain these differences. In a dvsaapi pipeline performing live convoy monitoring, the control plane decides which drone should focus on which vehicle, when to hand off tracking responsibilities between agents, and how to fuse observations from multiple drones into a shared semantic map. The ModelContextProtocol ensures that each perception model receives the correct camera pose, timestamp, and mission context so that object detections are consistent across drones. In agricultural monitoring, the control plane coordinates agents that detect crop stress, agents that plan optimal flight paths, and agents that trigger alerts to ground operators. The protocol ensures that the vegetationindex model receives the correct spectral calibration and environmental metadata. In emergency response, the control plane arbitrates between agents that detect survivors, agents that classify hazards, and agents that plan safe approach routes. The protocol ensures that thermalvision models receive the correct sensor context so that detections remain reliable under varying lighting conditions.

Their strengths also differ. A multiagent control plane excels at distributed decisionmaking, fault tolerance, and missionlevel coordination. It is the architectural mechanism that allows UAVs to behave like autonomous collaborators rather than isolated sensors. The ModelContextProtocol excels at reliability, reproducibility, and correctness of model calls. It prevents silent failures caused by missing metadata, inconsistent schemas, or ambiguous outputs. The control plane is about *behavior*; the protocol is about *grounding*. The control plane scales horizontally across agents; the protocol scales vertically across model invocations. The control plane handles negotiation, delegation, and arbitration; the protocol handles structure, context, and safety.

In drone video sensing systems, both are indispensable. Without a multiagent control plane, UAVs cannot coordinate complex missions or integrate multiple forms of intelligence. Without the ModelContextProtocol, perception models become brittle, ungrounded, and unreliable. Together, they form the foundation of modern agentic UAV architectures: one ensures that autonomous agents collaborate effectively, and the other ensures that each model call is contextually correct and operationally safe. Their interplay is what allows dvsaapi systems to deliver robust inflection detection, importancesampled analytics, and realtime geospatial intelligence across dynamic, uncertain environments.

Implementation: https://gitub.com/ravibeta/dvsa-api/pull/13


Saturday, July 4, 2026

 Diante Fuchs’s The Gift of Anxiety: Harnessing the EASE Method to Turn Stuck Anxiety into Your Greatest Ally, published by TCK in 2024, reframes anxiety not as an enemy to defeat but as a natural emotional signal designed to protect, guide, and inform us. Many people experience anxiety as something disruptive or shameful, and their first instinct is often to suppress it, escape it, or eliminate it entirely. Fuchs argues that this response misunderstands anxiety’s purpose. At its best, anxiety is an inner alarm that draws attention to something important and prompts constructive action. The challenge is not the presence of anxiety itself, but the way people respond when anxiety becomes the focus of fear.

Ordinary anxiety can be useful because it directs attention toward preparation and safety. Feeling nervous before an important meeting, for example, may motivate someone to prepare carefully, dress appropriately, plan a route, and arrive on time. Once those actions are taken, the anxiety usually subsides because its message has been received and addressed. In this sense, anxiety can help people navigate uncertainty with greater awareness and responsibility. Problems arise, however, when the alarm does not turn off and begins signaling about itself. This is what Fuchs calls “stuck anxiety”: a cycle in which people become anxious not only about a situation but about the fact that they are anxious. They may begin monitoring physical symptoms such as dizziness, nausea, a racing heart, or tightness in the chest, then interpret those sensations as signs of danger. The result is a loop in which anxiety about anxiety intensifies the original distress.

Stuck anxiety often develops through a recognizable pattern. At first, anxiety lingers longer than expected and produces fear and overwhelm. In response, a person may try urgently to reject or eliminate the feeling through coping strategies, reassurance, or treatment, but the goal of making anxiety disappear can create even greater sensitivity to its return. Over time, this sensitivity becomes hypervigilance, a constant checking for symptoms that can interfere with work, routines, and relationships. Eventually, the person may begin avoiding situations that trigger discomfort. Avoidance brings temporary relief, but it also teaches the brain that the avoided situation is dangerous, reinforcing the belief that the person cannot cope. Fear becomes frustration, frustration becomes fixation, fixation becomes exhaustion, and exhaustion leads to avoidance, which then restarts the cycle.

To break this cycle, Fuchs introduces the EASE method: Empower, Accept, Shift, and Engage. The first step, Empower, begins with understanding what anxiety is doing in the body. When the brain detects a threat, it releases adrenaline and activates the fight-or-flight response. The heart beats faster to move blood to the muscles, breathing quickens to supply more oxygen, digestion slows to conserve energy, and the body releases glucose for strength. Sensations such as shakiness, nausea, dizziness, chest tightness, or a racing heart are not proof that something is wrong; they are signs that the body is preparing to protect itself. Knowledge reduces fear because it transforms these sensations from mysterious threats into understandable survival responses.

Empowerment also means examining the roots of anxiety with curiosity rather than judgment. Fuchs compares anxiety to a plant. Biological factors such as genetics or hormones may be the seed, while environmental experiences provide the soil and present-day stressors provide the water. Painful memories, early patterns of uncertainty, current financial pressure, toxic relationships, or demanding work conditions can all create circumstances in which anxiety grows. By reflecting on these influences, people can better understand what their anxiety is trying to communicate. They can identify whether medical support, lifestyle changes, stronger boundaries, or deeper emotional work may be needed. This process turns anxiety into information rather than an enemy.

The second step, Accept, asks people to give anxiety space instead of pushing it away. Fuchs suggests that anxiety behaves like a frantic visitor at the door: ignoring the visitor or refusing to open the door only makes the knocking louder, while listening calmly allows the visitor to settle. Acceptance does not mean liking anxiety or giving it control. It means recognizing that anxiety is part of human experience and responding to it with compassion. Many people learned early in life that they had to be perfect, controlled, or emotionally composed to feel safe or loved. As adults, they can begin to reassure themselves that mistakes, strong emotions, and uncertainty are manageable. This kind of self-compassion helps soften anxiety’s intensity.

Acceptance also involves challenging the “what if” questions that fuel anxious spirals. Rather than avoiding frightening possibilities, Fuchs encourages asking, “So what if that happens?” and then following the chain of feared outcomes until it becomes clear that even difficult situations are often survivable and manageable. Someone who fears having a panic attack while driving may realize that they could pull over, breathe, call for help if needed, and wait for the sensations to pass. The experience may be uncomfortable, but it is not necessarily dangerous. By testing anxious assumptions against evidence, people can replace catastrophic beliefs with a steadier confidence in their ability to cope.

The third step, Shift, teaches people to redirect attention away from anxious monitoring and back toward the present. This does not require forcing anxiety to disappear. Instead, it involves allowing anxiety to exist while choosing to place attention elsewhere. Grounding practices such as the 5,4,3,2,1 method can help: noticing five things one can see, four things one can touch, three sounds one can hear, two scents one can smell, and one flavor one can taste. By engaging the senses, the mind reconnects with the immediate environment, the rational brain becomes more active, and the nervous system begins to settle.

Shifting also means questioning unhelpful thoughts and reconnecting with personal values. An anxious thought can be treated like an expensive purchase: before accepting it, a person can ask whether believing it is worth the cost. If the thought “I will lose control in public” leads to isolation and lost relationships, it may not be worth buying. Other techniques include mentally canceling the thought like a pop-up window, repeating it in a silly voice to reduce its power, or focusing on the facts of the present moment rather than imagined future disasters. Beyond individual thoughts, anxiety may also reveal a deeper misalignment between the life a person is living and the values that matter most. Someone may feel anxious because they are pursuing a career chosen to satisfy family expectations rather than their own need for creativity, freedom, connection, or growth. Clarifying values and taking steps toward them can restore balance and reduce inner conflict.

The final step, Engage, invites people to move toward what anxiety has taught them to avoid. Avoidance may feel protective in the short term, but over time it strengthens fear and narrows life. Engagement reverses this pattern through small, deliberate actions. The goal is not to wait until anxiety vanishes, but to act while anxiety is present and learn through experience that discomfort can be tolerated. A person who dreads calling the dentist may notice that postponing the appointment prolongs both physical pain and emotional stress. By making the call, scheduling the visit, and following through, they reduce avoidance’s power and build evidence that they can handle difficult tasks.

Fuchs recommends turning engagement into specific, measurable, achievable, relevant, and time-sensitive goals. If someone misses riding a bike but feels anxious about riding alone, the goal might be to ride twice a week for one hour at a time. Such goals are concrete, realistic, and trackable. Each completed action deserves recognition because celebration helps the brain associate facing anxiety with positive outcomes. Over time, these small victories create confidence, self-approval, and momentum. Anxiety loses authority as the person learns to move forward with it rather than organize life around avoiding it.

The power of this approach appears in Fuchs’s example of Nora, a once-confident assistant to a top CEO who lost her job and later succeeded in commercial property sales. Although she was outwardly doing well, she began experiencing morning dread and low motivation. Instead of treating anxiety as a symptom to suppress, Nora listened to what it revealed. She realized that working from home had left her lonely, that harsh self-talk intensified her distress, and that financial pressure made every morning feel threatening. In response, she joined a co-working space, practiced self-compassion, allowed herself restorative breaks and time with friends, and adjusted her finances to reduce pressure. By responding to anxiety with practical changes and emotional care, she created a healthier and more balanced life.

The Gift of Anxiety presents anxiety as a guide to deeper self-understanding. Anxiety points toward old beliefs, unresolved fears, present-day stressors, neglected needs, and avoided responsibilities. When people fight it, monitor it, or flee from it, the alarm grows louder. When they understand it, accept it, shift their attention, and engage with life deliberately, the alarm begins to quiet. Fuchs’s central message is that anxiety is not proof of weakness or failure. It is part of the emotional world, and when approached with knowledge, compassion, and courage, it can become a source of balance, growth, and healing.


Friday, July 3, 2026

 

Authenticity in the age of artificial intelligence is no longer a simple question of whether something was produced by a person or by a machine. It is becoming a question of agency, transparency, context, and intent. As generative systems increasingly assist with writing, image creation, speech, decision support, and interpersonal communication, the boundary between human expression and machine-mediated expression has become technically and socially ambiguous. A message may originate from a human idea but be refined by an algorithm; an image may depict a real event but be enhanced, compressed, or reconstructed by software; a profile, résumé, or professional statement may be truthful in substance while optimized in tone by an automated assistant. In this environment, authenticity should not be defined solely by the absence of automation. A more useful standard is whether the final artifact remains meaningfully connected to the human judgment, values, and accountability behind it.

From a technical perspective, AI challenges authenticity because it separates surface form from originating effort. Earlier digital tools changed how people edited, distributed, and formatted communication, but they did not usually generate the substance of expression at scale. Contemporary AI systems can produce fluent language, realistic media, and persuasive interaction patterns with minimal prompting. This creates a verification problem: the observable output may no longer reveal the process that produced it. A polished response may reflect careful human thought, automated pattern completion, or a hybrid of both. The same ambiguity applies to images, audio, video, and synthetic personas. As a result, audiences increasingly need process signals rather than relying only on content signals. Metadata, provenance standards, disclosure norms, and audit trails become important not because every automated contribution is deceptive, but because trust depends on understanding the relationship between representation and reality.

The professional risk is not that AI assistance exists, but that it can obscure responsibility. In business, education, research, design, and public communication, authenticity has traditionally carried an assumption of accountable authorship. Readers, customers, colleagues, and institutions evaluate not only what is said, but who stands behind it and how it was produced. When AI-generated or AI-shaped content is presented without appropriate context, accountability can become diffuse. A leader may appear more empathetic than their actual engagement supports. A candidate may seem more articulate than their underlying competence demonstrates. A brand may sound personal while operating through automated segmentation. These examples do not make AI inherently inauthentic; they show that authenticity depends on whether the use of AI amplifies genuine intention or substitutes for it in a way that misleads the audience.

A strong authenticity framework should therefore distinguish between assistance, augmentation, simulation, and deception. Assistance occurs when AI helps improve clarity, accessibility, translation, structure, or recall while the human remains the source of intent and final judgment. Augmentation occurs when AI expands a person’s expressive range, enabling communication that might otherwise be limited by language barriers, cognitive load, disability, time pressure, or lack of specialized production skills. Simulation occurs when AI creates the appearance of human presence, personality, expertise, or experience without the corresponding human basis. Deception occurs when that simulation is deliberately or negligently presented as something it is not. These categories are more practical than a binary distinction between human-made and machine-made content, because real workflows increasingly involve mixtures of human and computational contribution.

For organizations, the technical challenge is to design systems that preserve human agency rather than merely optimize engagement. Many AI tools are tuned to produce outputs that attract attention, increase response rates, or conform to statistically successful patterns. Those goals can be useful, but they can also flatten individuality and encourage formulaic communication. When optimization becomes the dominant design objective, authenticity erodes because people begin to communicate in the style most likely to perform well rather than in the style that best reflects their actual judgment. Responsible implementation requires explicit choices about when optimization is appropriate, when disclosure is necessary, and when human review is mandatory. It also requires recognizing that measurable engagement is not the same as trust, understanding, or meaningful connection.

Authenticity in 2025 should be treated as a socio-technical property rather than a nostalgic preference for unassisted creation. It emerges when people understand the role of AI in a communication, when the output remains aligned with the human values and decisions behind it, and when accountability is not hidden behind automation. The most credible future is not one in which AI is excluded from expression, but one in which AI is used with disciplined transparency and human control. A technically mature definition of being real must account for hybrid authorship while still protecting trust. In practice, this means that the question is not simply whether AI was used. The better question is whether the human remained meaningfully present in the choices, commitments, and consequences carried by the final work.

References:“AI & Authenticity: What Does It Mean to Be ‘Real’ in 2025?” written by Ben Szuhaj at Kungfu.ai in 2025

2.    

Thursday, July 2, 2026

 How to integrate reasoning models into dvsa-api

Integrate reasoning models into dvsa-api by adding a modular inference layer that routes requests to Azure Foundry/OpenAI reasoning deployments or to a hosted custom reasoning model via a lightweight orchestration policy, expose a unified prompt-and-observation interface in the API, and instrument token-level, provenance, and cost telemetry for safe production use.

The integration should treat reasoning models as first-class, stateful inference engines that produce both reasoning traces and final completions. Reasoning models improve complex decisioning, multi-step scene interpretation, and auditability because they explicitly generate internal reasoning tokens and verification steps; Azure Foundry and Azure OpenAI expose reasoning-specific controls (reasoning_effort, reasoning_tokens) and developer messages to tune effort and provenance. 

Start by adding a Reasoning Adapter inside dvsa-api that normalizes inputs (multi-frame metadata, object tracks, sensor confidence) into a compact context window and supports two modes: (1) explainable inference that returns reasoning traces for human review, and (2) actionable inference that emits structured actions (labels, bounding boxes, confidence, remediation steps). This mirrors ReAct-style interleaving of reasoning and actions to allow the model to query retrieval indices or call deterministic vision pipelines when needed. 

Operational design must include a model-orchestration policy that routes requests by cost, latency, and privacy: low-latency or PII-sensitive queries go to on-prem/custom models; high-complexity reasoning goes to Azure reasoning deployments; fallback rules and canary routing enable safe rollouts. Log model version, token counts, retrieval snapshot IDs, and reasoning_effort for every call to support audit and chargeback. 

Productionize with MLOps best practices: containerize custom reasoning models, expose a gRPC/REST inference shim, implement feature-store parity for any precomputed features, and add continuous monitoring for data drift, hallucination rates, and latency SLAs. Use shadow deployments and A/B canaries to validate reasoning trace quality against human labels before full rollout. 

Architectural trade-offs are straightforward: Azure reasoning models give superior out-of-the-box chain-of-thought and managed scaling but incur cloud cost and data residency constraints; custom models offer privacy and lower marginal cost at scale but require investment in quantization, inference optimization, and safety filters. A hybrid orchestration yields the best ROI for dvsa-api workflows that mix sensitive telemetry and high-complexity reasoning. 

Implementation roadmap: add the Reasoning Adapter and orchestration policy, wire Azure SDK clients and a pluggable custom-model shim, implement token- and trace-level telemetry, run a 4-week canary with shadow logging, then enable human-in-the-loop review for high-risk outputs. Key metrics to track: reasoning_tokens per request, hallucination incidents, latency P95, and model selection cost per inference.


Tuesday, June 30, 2026

Bulk mailer for mail campaigns

 // ── CONFIG ────────────────────────────────────────────────────────────────

const SHEET_NAME = "Contacts";

const DAILY_LIMIT = 500; // ← Change to 2000 if you have Google Workspace

const FROM_NAME = "Ravi Rajamani";


// Column indices (1-based, matching your sheet layout)

const COL_EMAIL = 1; // Column A: to_email

const COL_SUBJECT = 7; // Column G: subject

const COL_BODY = 8; // Column H: body

const COL_STATUS = 9; // Column I: Status


// ── MAIN FUNCTION ─────────────────────────────────────────────────────────

function sendCampaign() {

  const ss = SpreadsheetApp.getActiveSpreadsheet();

  const sheet = ss.getSheetByName(SHEET_NAME);


  if (!sheet) {

    Logger.log('ERROR: Sheet "' + SHEET_NAME + '" not found. Check the tab name.');

    return;

  }


  const lastRow = sheet.getLastRow();

  if (lastRow < 2) {

    Logger.log("No data rows found.");

    return;

  }


  // ── READ ALL DATA IN ONE CALL (the key fix) ───────────────────────────

  // This fetches the entire sheet as a 2D array in a single API call,

  // instead of making 4 individual calls per row inside the loop.

  const numRows = lastRow - 1; // exclude header row

  const allData = sheet.getRange(2, 1, numRows, COL_STATUS).getValues();

  // allData[i] is a 0-indexed array for row (i+2) of the sheet

  // allData[i][0] = to_email, [6] = subject, [7] = body, [8] = Status


  Logger.log("Data loaded. Total rows: " + numRows);


  // ── COLLECT ROWS TO SEND ──────────────────────────────────────────────

  // Build a list of {sheetRow, email, subject, body} for unsent rows only.

  // This entire pass is pure in-memory — no Sheets API calls.

  const toSend = [];

  for (let i = 0; i < allData.length; i++) {

    const status = String(allData[i][COL_STATUS - 1]).trim();

    if (status === "Sent") continue; // already sent

    if (status.startsWith("Error:")) continue; // skip permanent errors


    const email = String(allData[i][COL_EMAIL - 1]).trim();

    const subject = String(allData[i][COL_SUBJECT - 1]).trim();

    const body = String(allData[i][COL_BODY - 1]).trim();


    if (!email || !email.includes("@")) continue; // bad address

    if (!subject || !body) continue; // incomplete row


    toSend.push({ sheetRow: i + 2, email, subject, body }); // sheetRow is 1-based

  }


  Logger.log("Unsent rows found: " + toSend.length);


  if (toSend.length === 0) {

    Logger.log("Campaign complete — all rows have been sent.");

    return;

  }


  // ── SEND EMAILS ───────────────────────────────────────────────────────

  // Batch status updates: collect changes, then write them all at once

  // at the end of the run (or every 50 sends as a safety checkpoint).

  let sentThisRun = 0;

  let errorCount = 0;

  const statusUpdates = []; // {sheetRow, value}


  const limit = Math.min(DAILY_LIMIT, toSend.length);


  for (let j = 0; j < limit; j++) {

    const { sheetRow, email, subject, body } = toSend[j];


    try {

      MailApp.sendEmail({

        to: email,

        subject: subject,

        body: body,

        name: FROM_NAME

      });


      statusUpdates.push({ sheetRow, value: "Sent" });

      sentThisRun++;


      if (sentThisRun % 50 === 0) {

        // Checkpoint write every 50 sends so progress isn't lost if we time out

        flushStatusUpdates(sheet, statusUpdates);

        Logger.log("Checkpoint: " + sentThisRun + " sent so far...");

      }


    } catch (e) {

      statusUpdates.push({ sheetRow, value: "Error: " + e.message });

      errorCount++;

      Logger.log("FAILED row " + sheetRow + " (" + email + "): " + e.message);

    }


    // 200ms pause to stay within Gmail's per-second rate limit

    Utilities.sleep(200);

  }


  // ── FINAL WRITE ───────────────────────────────────────────────────────

  // Write any remaining status updates not yet flushed

  flushStatusUpdates(sheet, statusUpdates);


  // ── SUMMARY ───────────────────────────────────────────────────────────

  Logger.log("─────────────────────────────────────");

  Logger.log("Run complete.");

  Logger.log("Sent this run: " + sentThisRun);

  Logger.log("Errors this run: " + errorCount);

  Logger.log("Remaining: " + (toSend.length - sentThisRun));

  Logger.log("─────────────────────────────────────");

}


// ── HELPER: batch-write all pending status updates ─────────────────────────

// Clears the array after writing so we don't double-write on checkpoints.

function flushStatusUpdates(sheet, updates) {

  if (updates.length === 0) return;

  for (const { sheetRow, value } of updates) {

    sheet.getRange(sheetRow, COL_STATUS).setValue(value);

  }

  SpreadsheetApp.flush(); // force Sheets to commit writes immediately

  updates.length = 0; // clear in-place so the caller's array is emptied

}



Monday, June 29, 2026

 Understanding Perplexity’s “Computer” mode of execution for my drone video sensing applications:

When Perplexity introduced Computer, they described it as a digital worker capable of using “your computer to complete tasks for you.” Their public documentation and product pages emphasize that Computer can operate across desktop, mobile, Slack, and Microsoft 365, and that it can perform long running workflows such as research, document creation, email coordination, and scheduled jobs. The phrasing “uses your computer” naturally raises the question: does Perplexity install a local agent on the end user’s machine, similar to how Azure installs the Azure VM Agent or AWS installs the EC2 SSM Agent to gain visibility and control over compute resources?

Perplexity does not describe installing a privileged system level agent like Azure’s or AWS’s cloud management agents. Instead, their “uses your computer” capability appears to rely on a local execution harness embedded in the Perplexity desktop app, browser extension, or integration client. This harness exposes a controlled interface that the cloud hosted Computer agent can command. In other words, Perplexity does not install a daemon with OS level privileges; rather, it installs a client side sandbox that can perform user authorized actions such as opening tabs, filling forms, interacting with files the user selects, or automating workflows inside the boundaries of the app’s permissions.

This model is consistent with how Perplexity describes Computer’s security posture: tasks run in isolated environments, actions require explicit user permission, and the system is designed to avoid privileged access. It is also consistent with the fact that Computer runs identically on mobile, desktop, Slack, and Microsoft 365, which strongly suggests a portable client side execution layer rather than a deep OS level agent. The cloud agent plans and orchestrates tasks, but the actual execution on the user’s machine is mediated through the Perplexity client, which acts as a bridge between cloud intelligence and local capabilities.

The transition between cloud execution and local execution therefore follows a predictable pattern. The user issues a high level goal in the Perplexity interface. The cloud hosted Computer agent decomposes the goal into subtasks, selects appropriate skills, and determines which subtasks require local interaction. When a local action is needed—such as opening a browser window, filling a form, or manipulating a file—the cloud agent sends a structured command to the client side harness. The harness executes the action within the user’s environment, returns state information or results, and the cloud agent continues planning. This back and forth continues until the workflow completes, and because the cloud agent maintains persistent state, the workflow can run for hours or months without user supervision.

This hybrid model is conceptually similar to Azure’s and AWS’s cloud agents, but with a crucial difference. Azure VM Agent and AWS SSM Agent are privileged system services designed for infrastructure management, patching, telemetry, and remote command execution. Perplexity’s client harness is not a privileged agent; it is a user permissioned execution surface that allows the cloud agent to act as if it were a human user interacting with the machine. The similarity lies in the pattern: a cloud service orchestrates tasks, and a local component executes them. The difference lies in the depth of access and the security model.

How Perplexity transitions between cloud execution and local execution

To understand the transition mechanism, imagine a workflow such as booking travel. The user asks Computer to find flights, compare options, and complete the booking. The cloud agent performs the research using Perplexity’s search native intelligence and multi model orchestration. Once it identifies the desired itinerary, it needs to interact with the user’s browser or booking app. At this point, the cloud agent sends a command to the local harness: open the booking site, navigate to the correct page, fill in the traveler details, and wait for user confirmation before submitting payment. The harness executes these actions, streams back page content or DOM state, and the cloud agent continues reasoning. The workflow oscillates between cloud reasoning and local execution until the task is complete.

This pattern generalizes to any long running workflow. The cloud agent maintains the plan, memory, and schedule. The local harness provides access to the user’s environment. The two communicate through a secure channel, typically WebSocket or a similar bidirectional protocol. The cloud agent can pause, resume, retry, or escalate tasks. The local harness can request clarification or permission. The entire system behaves like a distributed agent with a cloud brain and local hands.

This architecture explains how Perplexity can run workflows for hours or months. The cloud agent persists state, monitors triggers, and schedules tasks. The local harness wakes when needed, executes actions, and returns results. The user does not need to keep the interface open; the agent continues working in the background.

Why this matters for my drone video sensing analytics system

My dvsa api application already embodies the computational core of drone analytics: ingesting video, extracting trajectories, detecting inflection signatures, computing importance sampling metrics, and producing observability outputs. What I want is the Perplexity style agentic layer: a system where users can define high level goals such as continuously analyzing flights, generating daily reports, or monitoring anomalies, and where an intelligent agent orchestrates long running workflows that span both cloud and local resources.

The Perplexity model provides a blueprint. I need a cloud hosted planner that decomposes user goals into subtasks, selects skills, and manages schedules. I need a local runner that exposes dvsa api and local compute resources to the cloud agent. I need a secure channel between the two. And I need a UX that presents this as “Your Drone Computer,” a mode where the agent can use the user’s machine to complete drone analytics tasks.

The local runner in my system would be analogous to Perplexity’s client harness. It would not be a privileged OS level agent like Azure VM Agent or AWS SSM Agent. Instead, it would be a user permissioned daemon or app that can access drone video files, run dvsa api, and return results. The cloud agent would orchestrate ingestion, analytics, reporting, and scheduling. The transition between cloud and local execution would follow the same pattern: cloud planning, local execution, cloud reasoning, and local execution.

This hybrid architecture is ideal for drone analytics because it allows us to combine cloud intelligence with local compute. Users can run dvsa api on their own GPU equipped machines while benefiting from cloud hosted planning, memory, and coordination. Long running workflows become possible: continuous ingestion of new flights, daily summaries, anomaly alerts, and scheduled reports. The agent can operate for hours or months, just like Perplexity’s Computer.


Sunday, June 28, 2026

 Sample Google Apps Script to send out bulk mails:

// ── CONFIG ────────────────────────────────────────────────────────────────
const SHEET_NAME  = "Contacts";
const DAILY_LIMIT = 500;        // ← Change to 2000 if you have Google Workspace
const FROM_NAME   = "Ravi Rajamani";

// Column indices (1-based, matching your sheet layout)
const COL_EMAIL   = 1;  // Column A: to_email
const COL_SUBJECT = 7;  // Column G: subject
const COL_BODY    = 8;  // Column H: body
const COL_STATUS  = 9;  // Column I: Status

// ── MAIN FUNCTION ─────────────────────────────────────────────────────────
function sendCampaign() {
  const ss    = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName(SHEET_NAME);

  if (!sheet) {
    Logger.log('ERROR: Sheet "' + SHEET_NAME + '" not found. Check the tab name.');
    return;
  }

  const lastRow = sheet.getLastRow();
  if (lastRow < 2) {
    Logger.log("No data rows found.");
    return;
  }

  // ── READ ALL DATA IN ONE CALL (the key fix) ───────────────────────────
  // This fetches the entire sheet as a 2D array in a single API call,
  // instead of making 4 individual calls per row inside the loop.
  const numRows = lastRow - 1; // exclude header row
  const allData = sheet.getRange(2, 1, numRows, COL_STATUS).getValues();
  // allData[i] is a 0-indexed array for row (i+2) of the sheet
  // allData[i][0] = to_email, [6] = subject, [7] = body, [8] = Status

  Logger.log("Data loaded. Total rows: " + numRows);

  // ── COLLECT ROWS TO SEND ──────────────────────────────────────────────
  // Build a list of {sheetRow, email, subject, body} for unsent rows only.
  // This entire pass is pure in-memory — no Sheets API calls.
  const toSend = [];
  for (let i = 0; i < allData.length; i++) {
    const status = String(allData[i][COL_STATUS - 1]).trim();
    if (status === "Sent") continue;                          // already sent
    if (status.startsWith("Error:")) continue;               // skip permanent errors

    const email   = String(allData[i][COL_EMAIL   - 1]).trim();
    const subject = String(allData[i][COL_SUBJECT - 1]).trim();
    const body    = String(allData[i][COL_BODY    - 1]).trim();

    if (!email || !email.includes("@")) continue;            // bad address
    if (!subject || !body) continue;                         // incomplete row

    toSend.push({ sheetRow: i + 2, email, subject, body });  // sheetRow is 1-based
  }

  Logger.log("Unsent rows found: " + toSend.length);

  if (toSend.length === 0) {
    Logger.log("Campaign complete — all rows have been sent.");
    return;
  }

  // ── SEND EMAILS ───────────────────────────────────────────────────────
  // Batch status updates: collect changes, then write them all at once
  // at the end of the run (or every 50 sends as a safety checkpoint).
  let sentThisRun = 0;
  let errorCount  = 0;
  const statusUpdates = []; // {sheetRow, value}

  const limit = Math.min(DAILY_LIMIT, toSend.length);

  for (let j = 0; j < limit; j++) {
    const { sheetRow, email, subject, body } = toSend[j];

    try {
      MailApp.sendEmail({
        to:      email,
        subject: subject,
        body:    body,
        name:    FROM_NAME
      });

      statusUpdates.push({ sheetRow, value: "Sent" });
      sentThisRun++;

      if (sentThisRun % 50 === 0) {
        // Checkpoint write every 50 sends so progress isn't lost if we time out
        flushStatusUpdates(sheet, statusUpdates);
        Logger.log("Checkpoint: " + sentThisRun + " sent so far...");
      }

    } catch (e) {
      statusUpdates.push({ sheetRow, value: "Error: " + e.message });
      errorCount++;
      Logger.log("FAILED row " + sheetRow + " (" + email + "): " + e.message);
    }

    // 200ms pause to stay within Gmail's per-second rate limit
    Utilities.sleep(200);
  }

  // ── FINAL WRITE ───────────────────────────────────────────────────────
  // Write any remaining status updates not yet flushed
  flushStatusUpdates(sheet, statusUpdates);

  // ── SUMMARY ───────────────────────────────────────────────────────────
  Logger.log("─────────────────────────────────────");
  Logger.log("Run complete.");
  Logger.log("Sent this run:   " + sentThisRun);
  Logger.log("Errors this run: " + errorCount);
  Logger.log("Remaining:       " + (toSend.length - sentThisRun));
  Logger.log("─────────────────────────────────────");
}

// ── HELPER: batch-write all pending status updates ─────────────────────────
// Clears the array after writing so we don't double-write on checkpoints.
function flushStatusUpdates(sheet, updates) {
  if (updates.length === 0) return;
  for (const { sheetRow, value } of updates) {
    sheet.getRange(sheetRow, COL_STATUS).setValue(value);
  }
  SpreadsheetApp.flush(); // force Sheets to commit writes immediately
  updates.length = 0;     // clear in-place so the caller's array is emptied
}