# Agent tool timeouts: दोबारा होने से रोकने वाली recovery plans

Timeout client के बारे में एक observation है, काम के बारे में verdict नहीं। Caller ने response का इंतजार करना छोड़ दिया। उसे बस इतना ही पता है। जब कोई agent इस observation को "failed" मानकर state बदलने वाली call को retry करता है, तो दूसरा payment, दूसरा deployment, दूसरा support ticket या पहले से दबाव में चल रही machine पर दो बार चलने वाली remote command बन सकती है।

Agent tool timeouts के लिए recovery plan जरूरी है, क्योंकि agents उन्हें supervise करने वाले लोगों से तेज काम करते हैं और tool output को ground truth मान लेते हैं। Missing response ground truth नहीं है। Recovery path को तय करना होगा कि retry करना है, इंतजार करना है, evidence पूछना है या रुककर human decision मांगना है। Agent को consequential calls की अनुमति देने से पहले यह path तैयार करें।

## Timeout के बाद तीन संभावित स्थितियां

Client timeout के बाद request की तीन व्यापक स्थितियों में से एक हो सकती है: सेवा को request मिली ही नहीं, सेवा को request मिल गई लेकिन वह अभी पूरी नहीं हुई, या सेवा ने काम पूरा कर दिया लेकिन client को result नहीं मिला। Network failure connection खुलने से पहले, request body के रास्ते में, service के काम करते समय या response लौटते समय हो सकती है। एक ही exception type इन सभी स्थितियों को दिखा सकती है।

यह अंतर अगली कार्रवाई बदल देता है। यदि किसी connection से पहले DNS lookup विफल हुआ है, तो retry उचित हो सकता है। यदि सेवा ने resource delete करने वाली request स्वीकार की और response गायब हो गया, तो retry destructive operation को दोहरा सकता है। यदि सेवा ने asynchronous job queue में डाल दी है, तो दूसरी submission पहली के चलने के दौरान competing job बना सकती है।

HTTP में ऐसा कोई magic bit नहीं है जो client को बता दे कि इनमें से कौन सी स्थिति हुई। RFC 9110 request methods और responses के अर्थ का वर्णन करता है, जिसमें safe और idempotent methods का अंतर भी शामिल है। यह वादा नहीं करता कि response खो जाने पर client server execution का अनुमान लगा सकता है। यह सीमा भौतिक है, किसी missing SDK option की वजह से नहीं।

Teams अक्सर दो अलग सवालों को मिला देती हैं:

- क्या इस request को दोबारा भेजने से intended final state बदले बिना काम हो जाएगा?
- क्या मूल प्रयास वास्तव में सेवा तक पहुंचा और उस पर असर डाला?

Idempotency पहले सवाल का उत्तर देती है। Reconciliation दूसरे का। System को दोनों चाहिए। Idempotent `PUT` को दोहराना सुरक्षित हो सकता है, फिर भी timeout के बाद यह पता न चले कि उस request से शुरू हुआ downstream work पूरा हुआ या नहीं। Status lookup outcome स्थापित कर सकता है, लेकिन अगर सेवा दो indistinguishable creates स्वीकार कर ले तो वह duplicate work से नहीं बचाता।

Agents के tool contracts में यह अंतर साफ होना चाहिए। `request timed out` जैसी plain text error agent को अनुमान लगाने के लिए छोड़ देती है। `outcome: unknown` वाला structured result agent को बताता है कि उसे retry branch छोड़कर evidence branch में जाना है।

## Retry चुनने से पहले request path का नक्शा बनाएं

एक उपयोगी recovery plan उन सीमाओं को नाम देता है जहां evidence मिल सकता है। Agent process से शुरू करें, फिर tool wrapper, connection pool, gateway या proxy, service ingress, application, durable store और asynchronous work संभालने वाले worker तक जाएं। एक boundary पर हुआ timeout अगली boundary के बारे में कोई भरोसेमंद जानकारी नहीं देता।

Deployment create करने वाली call पर विचार करें। Agent tool के जरिए request भेजता है। Client पूरी request body लिखता है, server deployment record commit करता है, फिर client तक response पहुंचने से पहले connection टूट जाता है। Tool timeout लौटाता है। Agent नई request के साथ retry करता है। अब server के पास दो deployment records हैं, और दोनों अपने-अपने दृष्टिकोण से valid हैं।

अब एक detail बदलें: body upload करते समय client timeout हो जाता है और application code चलने से पहले server partial body reject कर देता है। वही tool फिर भी `timeout` लौटा सकता है। इस स्थिति में retry से ठीक एक deployment बन सकता है। केवल timeout देखकर caller इन दोनों स्थितियों में अंतर नहीं कर सकता।

हर component से मिलने वाले evidence को लिखें। सामान्य HTTP action में इसमें ये चीजें शामिल हो सकती हैं:

- Client timestamps, चुना गया target, request body digest और caller द्वारा बनाया गया operation ID।
- Service access logs, जिनसे पता चले कि ingress ने request स्वीकार की या नहीं।
- Application record, जिसमें operation ID को committed result के साथ रखा गया हो।
- उन actions के worker या queue records जो synchronous request के बाद भी चलते रहते हैं।
- ऐसा read endpoint जो current state या operation status लौटाए।

Network logs को अकेला source of truth न बनाएं। Load balancer log दिखा सकता है कि bytes पहुंचे, लेकिन यह साबित नहीं कर सकता कि database transaction commit हुई। Database record commit साबित कर सकता है, लेकिन यह नहीं कि बाद में external provider को side effect मिला। Authoritative record उसी action से मेल खाना चाहिए जिसे आप साबित करना चाहते हैं।

Email भेजने के लिए provider का accepted-message identifier, application log में लिखे "about to send" से मजबूत evidence है। Database migration के लिए migration table या transaction record उस shell process exit code से मजबूत है जिसे caller ने कभी पाया ही नहीं। Cloud resource create के लिए caller-generated ID वाला operation URL या resource tag, बार-बार create request भेजने से बेहतर है।

## Idempotency attempt की नहीं, operation की property होनी चाहिए

Retry scheme तभी काम करती है जब एक intended action की हर attempt में वही durable identifier हो। पहली network call से पहले identifier बनाएं। उसे action description के साथ persist करें। Process restart, tool restart या human operator को handoff के बाद भी उसे reuse करें।

Retry loop के अंदर नया UUID न बनाएं। Code review में यह pattern सावधान दिखाई देता है, लेकिन पूरा उद्देश्य खत्म कर देता है। Server हर retry को नई request समझता है और इसी तरह duplicate operations बनते हैं।

API के अनुसार request में idempotency value header या body field में भेजी जा सकती है। Transport detail से अधिक जरूरी server rule है। Server को उस value को operation और उसके result के साथ atomically जोड़ना चाहिए। अगर दो identical requests एक साथ आएं, तो server को उन्हें serialize करना चाहिए या एक को दूसरे का result दिखाना चाहिए। Delayed retries पूरी होने से पहले expire हो जाने वाला cache भरोसेमंद duplicate protection नहीं देता।

एक व्यावहारिक HTTP request इस तरह दिख सकती है:

```http
POST /deployments HTTP/1.1
Content-Type: application/json
Idempotency-Key: op_7d5d4d8e4e5a
X-Correlation-ID: run_42_task_9

{"repository":"api","revision":"a1b2c3d4","environment":"staging"}
```

Server को idempotency value के साथ meaningful request fields का fingerprint और resulting deployment या operation ID सुरक्षित रखना चाहिए। यदि वही value अलग revision या environment के साथ आए, तो उसे reject करें। अलग payload के लिए पहला result लौटाना गलत intent को चुपचाप लागू करना है।

Asynchronous operation के लिए server के work स्वीकार करते ही durable operation reference लौटाएं:

```json
{
  "operation_id": "dep_1842",
  "state": "accepted",
  "status_url": "/operations/dep_1842"
}
```

Timeout के बाद agent किसी दूसरी submission पर विचार करने से पहले `op_7d5d4d8e4e5a` या `dep_1842` के लिए query करता है। अगर API idempotency value या external reference से lookup, दोनों में से कुछ भी support नहीं करती, तो write को design के अनुसार ambiguous मानें। Disposable test resource के लिए यह स्वीकार्य हो सकता है। Cost पैदा करने या production state बदलने वाली autonomous action के लिए यह खराब विकल्प है।

किसी method को केवल इसलिए safe न मानें कि वह `POST` इस्तेमाल करती है और retry library लगी हुई है। HTTP method names intended semantics के संकेत हैं, ऐसे server implementation से सुरक्षा नहीं जो work को duplicate कर दे। Specific API documentation पढ़ें और duplicate behavior खुद test करें।

## Agents को explicit unknown-outcome state दें

जिस tool से बाहरी दुनिया प्रभावित हो सकती है, उसे agent को केवल `success` या `error` नहीं देना चाहिए। उसे तीसरा result भी चाहिए: `unknown`। यह state सबसे नुकसानदेह model behavior को रोकती है, जिसमें agent अधूरे transcript को उसी command के थोड़े बदले हुए रूप को आजमाने की अनुमति समझ लेता है।

ऐसा result contract इस्तेमाल करें जो विफल phase दर्ज करे और यह भी माने कि phase निश्चित नहीं हो सकता। उदाहरण:

```json
{
  "outcome": "unknown",
  "operation_id": "op_7d5d4d8e4e5a",
  "correlation_id": "run_42_task_9",
  "transport_observation": "response deadline exceeded after request write",
  "retry_allowed": false,
  "reconcile": {
    "method": "GET",
    "path": "/operations/by-id/op_7d5d4d8e4e5a"
  }
}
```

`retry_allowed` field tool या action definition से आना चाहिए, agent के English verbs के अनुमान से नहीं। Agent सुरक्षित रूप से यह नहीं मान सकता कि `create_release` harmless है क्योंकि target staging environment है। Staging deployment भी notifications भेज सकता है, shared quota खर्च कर सकता है या release channel बदल सकता है।

Agent को सीमित recovery sequence का पालन कराएं:

1. Intended action, operation ID, target और timeout observation को run record में सुरक्षित रखें।
2. उसी operation ID या service-issued reference से authoritative status source को query करें।
3. केवल confirmed terminal result पर आगे बढ़ें। तभी retry करें जब action definition इसकी अनुमति दे और status source दिखाए कि कोई operation स्वीकार नहीं हुई।
4. जब action की recovery deadline के भीतर service outcome स्थापित न कर सके, तो रुकें और evidence प्रस्तुत करें।

Stop condition जरूरी है। जो agent हमेशा poll करता रहे, वह attention खर्च कर सकता है और मूल उद्देश्य खत्म होने के बाद भी task को जीवित रख सकता है। जो agent write request के पांच रूप आजमाए, वह किसी और के लिए cleanup project बना सकता है। हर operation को request deadline से अलग recovery deadline दें।

Human approval अपने-आप unknown outcome ठीक नहीं करती। Approval इस सवाल का उत्तर देती है, "क्या यह caller यह action करने की कोशिश कर सकता है?" वह यह नहीं बताती, "क्या पिछला प्रयास सफल हुआ?" Authorization evidence और execution evidence को UI और logs दोनों में अलग रखें।

## Read timeouts और writes को अलग तरह से संभालें

Timed-out read में आम तौर पर duplicate risk कम होता है, लेकिन agent के फैसले फिर भी गलत हो सकते हैं। Agent resources list करते समय timeout हो सकता है, कहीं और से incomplete या stale cached response मिल सकता है और वह निष्कर्ष निकाल सकता है कि resource मौजूद नहीं है। फिर वह ऐसा create करने की कोशिश करता है जो वास्तविकता से टकरा जाता है।

Reads को उस decision के आधार पर वर्गीकृत करें जिसे वे support करती हैं। Harmless dashboard refresh सीमित backoff के साथ retry हो सकता है। Write जारी करने का निर्णय लेने वाली read के लिए स्पष्ट consistency rule चाहिए। यदि service ETag, version, generation number या read-after-write status endpoint देती है, तो उसका इस्तेमाल करें। यदि वह केवल eventual consistency देती है, तो waiting period और failure condition agent को स्पष्ट दिखाएं।

Universal retry count से बचें। छोटा metadata lookup दो quick retries सह सकता है। Warehouse लोड करने वाली report query को एक लंबी deadline और बिना तत्काल repeat के चलाना बेहतर हो सकता है। `429 Too Many Requests` या explicit service retry value लौटाने वाली call को socket timeout से अलग तरह से संभालना चाहिए। हर error को transient network issue मानना agent के जरिए partial outage को avoidable load में बदल देता है।

Backoff services की सुरक्षा में मदद करता है, लेकिन ambiguity हल नहीं करता। वह duplicate requests के बीच अधिक समय रखता है। महत्वपूर्ण हर write के लिए backoff को idempotency value या status check के साथ इस्तेमाल करें।

जहां API support करे, conditional writes इस्तेमाल करें। ज्ञात ETag के साथ `If-Match` request agent को read के बाद बदले गए resource को overwrite करने से रोक सकती है। Client-selected resource ID स्वीकार करने वाला create retry को एक ही object पर converge करा सकता है। ये mechanisms state transitions की रक्षा करते हैं, लेकिन उस resource के बाहर हुए side effects का record नहीं बनते।

## SSH remote execution को एक टूटे हुए stream के पीछे छिपा देता है

SSH timeout recovery में HTTP recovery से अधिक सावधानी चाहिए। Lost SSH session remote host के command शुरू करने के बाद, output transfer के दौरान, command exit होने के बाद या parent disconnect होने पर child process जारी रहने के दौरान हो सकता है। Local shell message यह नहीं बता सकती कि इनमें से कौन सी स्थिति हुई।

खतरनाक pattern एक लंबी compound command है:

```sh
ssh deploy@host 'download-release && migrate-db && restart-service'
```

यदि connection `migrate-db` के बाद टूटता है, तो पूरी command दोबारा चलाने से migration दो बार चल सकती है या ऐसा service restart हो सकता है जिसका नया release अभी download ही नहीं हुआ। Terminal transcript कई state transitions को एक opaque result में समेट देता है।

Remote work को durable और inspectable markers वाली operations में बांटें। Deployment शुरू होने से पहले release ID दर्ज कर सकता है, database में migration versions रख सकता है और local status command से active revision दिखा सकता है। Recovery tool कोई भी काम करने से पहले reconnect होकर इन markers को देखता है।

उदाहरण के लिए agent ऐसी remote status command इस्तेमाल कर सकता है जिसका output इंसानों के बजाय machines के लिए बनाया गया हो:

```sh
ssh deploy@host '/usr/local/bin/release-status --json'
```

```json
{
  "release_id": "rel_202",
  "phase": "migrated",
  "active_revision": "9f24c1",
  "migration_version": "20250308_02"
}
```

अब recovery action के पास निर्णय का आधार है। यदि `phase` `migrated` है, तो migrations दोबारा न चलाएं। यदि host कोई `release_id` नहीं बताता, तो agent operation तभी शुरू कर सकता है जब remote command यह guarantee देती हो कि absence का मतलब prior execution न होना है। यदि SSH reconnect नहीं कर सकता, तो सही result फिर भी unknown है। Production host बदलने वाली action में आशावादी rerun को सही उत्तर न बनाएं।

Remote locks का सावधानी से इस्तेमाल करें। Lock simultaneous runs रोक सकता है, लेकिन host failure के बाद stale lock recovery रोक सकता है। Lock record में operation ID और expiry policy रखें और lock को बिना सोचे हटाए inspection संभव बनाएं। हर पुराने lock को हटाने वाली cleanup command भी state-changing operation है और उसके अपने evidence की जरूरत है।

Sallyport agent process से SSH credentials बाहर रख सकता है, जबकि उसका bundled `sp-ssh` helper connection करता है, लेकिन credential isolation dropped session को दोबारा चलाने के लिए सुरक्षित नहीं बनाता। Remote command को अभी भी operation ID, durable markers और reconciliation route चाहिए।

## Audit records जांच में मदद करते हैं, लेकिन completion साबित नहीं करते

Action log में इतनी जानकारी रखें कि secrets सुरक्षित रखते हुए intent और recovery दोबारा समझी जा सके। Caller session, समय, target identity, operation ID, request digest या command template, authorization event, transport result और final reconciled outcome दर्ज करें। Bearer tokens, private keys या ऐसे raw request bodies दर्ज न करें जिनमें credentials या personal data हो सकते हैं।

Attempted action और completed action को अलग रखें। `POST /deployments timeout` लिखी log line attempt record है। बाद की query, जिसमें operation `succeeded` state में लौटती है, completion evidence है। दोनों रखें। पहले record को final success से बदलने पर incident का सबसे उपयोगी हिस्सा मिट जाता है: वह अवधि जब caller को पता नहीं था।

जब agent run से बाद में विवाद पैदा हो, तो tamper evidence महत्वपूर्ण होती है। आपको बताना होगा कि call किस process ने की, उसे क्या करने की अनुमति थी, उसे result मिला या नहीं और team ने final state कैसे स्थापित की। Mutable activity table खोजना आसान है, लेकिन यदि compromised process history बदल सकती है तो वह कमजोर evidence है।

Sallyport agent sessions और individual calls को write-blind encrypted, hash-chained audit log में दर्ज करता है। `sp audit verify` ciphertext पर offline chain verify कर सकता है। यह record gateway action और caller history दिखा सकता है, जबकि intended work पूरा हुआ या नहीं, इसका authority remote service या host के पास रहता है।

Gateway audit event को application transaction न समझें। Gateway ने outbound request log की हो, फिर भी service के commit करने से पहले request विफल हो सकती है। Service ने commit किया हो, फिर भी gateway ने response देखा न हो। जांच तब प्रभावी होती है जब दोनों तरफ के records में operation या correlation ID साझा हो।

## Outage से पहले ambiguity को जानबूझकर test करें

जिस timeout plan ने कभी lost response का सामना नहीं किया, वह केवल assumption है। उस exact failure को test करें जिसमें service operation पूरा करती है लेकिन client result खो देता है। अधिकांश teams इस case को छोड़ देती हैं, क्योंकि सामान्य happy-path test doubles इसे दिखा नहीं पाते।

ऐसा test endpoint या proxy fixture बनाएं जो request स्वीकार करे, durable record commit करे, फिर response में delay करे या उसे drop कर दे। वही operation ID दो बार भेजें। Verify करें कि service एक logical operation लौटाती है, agent timeout के बाद status query करता है और audit trail दोनों attempts तथा reconciliation result सुरक्षित रखता है।

इसके विपरीत स्थिति भी test करें: service के स्वीकार करने से पहले request interrupt करें। Confirm करें कि recovery तभी retry कर सकती है जब उसे कोई operation record न मिले। दोनों tests से client को एक ही exception मिल सकती है। Tool contract को अलग actions की ओर ले जाना चाहिए, क्योंकि service evidence अलग है।

इन cases को भी आजमाएं:

- Service operation स्वीकार करती है, लेकिन उसका worker agent की recovery deadline के बाद भी pending रहता है।
- दो agent processes लगभग एक ही समय पर वही operation ID submit करती हैं।
- Primary write endpoint स्वस्थ है, लेकिन status endpoint उपलब्ध नहीं है।
- SSH command child process शुरू करती है, फिर final output से पहले connection समाप्त हो जाता है।
- Human paused run फिर शुरू करता है, जबकि कोई दूसरा operator action को पहले ही reconcile कर चुका है।

आखिरी case real operations की एक समस्या पकड़ता है: recovery state agent की conversational memory के बाहर रहनी चाहिए। Operation ID और current finding को durable run record में रखें। Restart हुआ agent उस record को पढ़कर reconciliation जारी रखे, न कि पिछला transcript न दिखने पर नई action गढ़े।

उन unknown outcomes पर alerts लगाएं जो recovery deadlines पार कर जाएं। हर पहले timeout पर alert न करें, अगर सामान्य retries safe reads को हल कर देती हैं। तब alert करें जब consequential write की confirmed terminal state न हो, अलग payload वाली duplicate operation IDs आएं या remote markers expected progression से टकराएं। Agent के आगे बढ़ने से पहले इन्हीं मामलों में व्यक्ति की जरूरत होती है।

## Recovery plan में refusal सामान्य होना चाहिए

सबसे मजबूत timeout behavior अक्सर refusal होता है: "मैं पुष्टि नहीं कर सकता कि deployment request पूरी हुई या नहीं, इसलिए दूसरी request submit नहीं करूंगा।" यह tool failure नहीं है। Irreversible या महंगी action के आसपास evidence न होने पर यही सही response है।

इस response को उपयोगी बनाएं। Operation ID, target, अंतिम confirmed state, timestamps और वह exact status query या remote inspection दिखाएं जो स्थिति स्पष्ट कर सकती है। यदि कोई authoritative query मौजूद नहीं है, तो यह बात साफ कहें और निर्णय ऐसे व्यक्ति तक पहुंचाएं जो duplicate के परिणाम को समझता हो।

Teams इसका विरोध करती हैं क्योंकि retry productive लगता है और pause धीमा। पर्याप्त duplicate writes और अधूरे deployments के बाद trade-off साफ हो जाता है। Reconcile करने में लगा एक मिनट उस स्थिति से सस्ता है जिसमें दो systems यह मानते हैं कि उन्होंने वह एक action कर दी जिसे agent से करने को कहा गया था।

सबसे पहले उन writes से शुरुआत करें जो money movement, external messages, releases, access changes और data deletion करती हैं। हर एक के लिए API owner से तीन उत्तर मांगें: कौन सा identifier retries को एक operation से जोड़ता है, caller result कहां query कर सकता है और connection टूटने पर कौन सा proof उपलब्ध है। कोई उत्तर न मिले तो tool को `unknown` लौटाने दें और agent को अनुमान लगाना सिखाने के बजाय सोच-समझकर human decision मांगें।
