Octofs 0.14: इंतज़ार कोई tool call नहीं है
एजेंट ने test suite चलाया। Test suite चार मिनट लेता है। MCP client का idle timeout साठ सेकंड है।
आप समझ ही गए होंगे कि यह कहाँ जा रहा है। साठवें सेकंड पर client ने call cancel कर दी। Process चलती रही — उसे रुकने को किसी ने कहा ही नहीं — जबकि मॉडल ने, जिसके हाथ में test results की जगह एक cancellation थी, वही किया जो समझदारी लगती है: suite दोबारा चला दिया। दो test suites, एक ही directory, एक ही build artifacts पर एक-दूसरे से race करती हुईं। दूसरी locking error के साथ fail हुई, मॉडल ने रिपोर्ट कर दिया कि tests टूटे हुए हैं — और tests बिल्कुल ठीक थे।
एक और सेशन में उसी मॉडल ने, पिछली बार का जला हुआ, एक workaround निकाल लिया: build चलाओ, फिर sleep 240 call करो, फिर देखो। एक ऐसी tool call जो कुछ नहीं करती, चार मिनट तक खुली रखी गई, ताकि किसी दूसरी tool call के पास शायद दिखाने को कुछ हो। मॉडल ने polling का दोबारा आविष्कार कर लिया था, वह भी बुरे ढंग से — क्योंकि हमने उसे इससे बेहतर कुछ दिया ही नहीं था।
पिछली बार जब हमने octofs के बारे में लिखा, विषय था ग़लत लाइन पर जा गिरने वाली एडिट्स। वह लेख एक सिद्धांत पर ख़त्म हुआ था: MCP server का असली interface हर वह string है जो वह मॉडल को थमाता है। उसके बाद की ग्यारह releases — 0.10.1 से 0.14.1 तक, दो हफ़्ते — वही सिद्धांत सबसे धीमी string पर लागू करती हैं: वह string जिसका मॉडल इंतज़ार करता है। Shell अब event-driven है। Commands foreground में शुरू होती हैं, दस सेकंड से लंबी खिंचें तो अपने आप background में चली जाती हैं, और ख़त्म होने पर client को notification मिलती है। कुछ block नहीं होता, कुछ मारा नहीं जाता, कुछ दो बार नहीं चलता।
हम यहाँ तक कैसे पहुँचे, वह आगे है — ग़लत मोड़ समेत।
पहला fix: साबित करो कि call ज़िंदा है
साठ सेकंड वाली cancellation की एक उथली वजह थी और एक गहरी। उथली यह: shell call स्वभाव से ही ख़ामोश होती है। Compile हो रही build मिनटों तक wire पर कुछ नहीं कहती, और MCP client के लिए ख़ामोशी और hang हुए server में कोई फ़र्क़ नहीं। तो 0.10.2 ने liveness heartbeats जोड़े — जब तक command foreground में चलती है, octofs हर दस सेकंड पर एक progress notification भेजता है, हर समझदार idle timeout से काफ़ी नीचे, ताकि एक अकेली चूकी हुई beat call को cancel न करा सके।
इससे हत्याएँ रुक गईं। गहरी समस्या जस की तस रही: call अब भी block करती थी। चार मिनट का test suite अब भी session के चार मिनट खाता था, जिनमें मॉडल कुछ नहीं कर सकता था — न fail होती फ़ाइल पढ़ना, न अगली एडिट तैयार करना, न सोचना। Heartbeats इंतज़ार को झेलने लायक बनाते हैं। उपयोगी नहीं बनाते।
दूसरा fix: background jobs — और वह flag जिसे हमें हटाना पड़ा
0.11.0 ने background execution पेश किया: command को job के रूप में चलाओ, तुरंत एक handle वापस पाओ, output बाद में इकट्ठा करो। हर job एक MCP resource है, octofs://jobs/17342-1 जैसे URI के साथ, जिसे status और output के लिए कभी भी पढ़ा जा सकता है।
यह shell tool पर एक background flag के साथ shipped हुआ, और वह flag एक ऐसी ग़लती थी जिसे पीछे मुड़कर देखने पर हम एक जानी-पहचानी ग़लती के रूप में पहचानते हैं। 0.9.0 में हमने --line-mode switch इसलिए हटाया था कि जो सुरक्षा किसी flag के पीछे भेजी जाती है, उसे ज़्यादातर लोग कभी चालू नहीं करते। background flag वही बग था, बस दूसरे कपड़ों में: वह मॉडल से माँगता था कि command चलाने से पहले उसकी अवधि की भविष्यवाणी करे। मॉडल इसमें ठीक वैसे ही बुरे हैं जैसा आप उम्मीद करेंगे — warm cache पर cargo build पलक झपकते हो जाता है और cold पर छह मिनट लेता है, और flag ने इस न-जानने-लायक तथ्य को एक अनिवार्य फ़ैसला बना दिया। तेज़ command के लिए background का अंदाज़ा लगाओ, और आपने एक बेमतलब round trip जोड़ दिया। धीमी के लिए foreground का अंदाज़ा लगाओ, और आप वहीं लौट आए जहाँ से चले थे — blocked call पर।
तो 0.13.0 ने flag हटा दिया और भविष्यवाणी की जगह माप रख दी। हर command foreground में शुरू होती है। अगर वह दसवें सेकंड पर भी चल रही है, तो उसे अपने आप background job में promote कर दिया जाता है — वही process, न मारी गई, न restart हुई। Output capture पहले byte से durable है, इसलिए deadline पार करने में कुछ नहीं खोता: command ने अपनी foreground ज़िंदगी में जो भी छापा, वह job के log में बैठा मिलता है जब आप उसे बाद में पढ़ते हैं।
Promotion पर tool call तुरंत लौट आती है, एक ऐसे resource link के साथ जिसके name में command दर्ज है — ताकि client "make test … still running" render कर सके, बिना यह दोबारा निकाले कि job थी क्या, context compaction के बाद भी। Process के exit होने पर octofs job के URI के लिए notifications/resources/updated भेजता है। Client resource को एक बार पढ़ता है और उसे exit code और output की tail मिल जाती है। न polling, न खुली रखी call, न orphaned process।
उस फ़्लो के दो ब्योरों ने अपनी जगह मुश्किल रास्ते से कमाई है:
- Tail, head नहीं। Resource read output के आख़िरी 30 KB से ज़्यादा नहीं लौटाती। Build logs लंबे चलते हैं, और फ़ैसला — error, आख़िरी test summary — अंत में रहता है। जिस log की आख़िरी लाइन
FAILEDकहती है, उसके पहले 30 KB मॉडल को खिलाना ही वह तरीक़ा है जिससे आपको आत्मविश्वास से भरी रिपोर्ट मिलती है कि सब कुछ पास हो गया। - दो delivery paths। 2026-07-28 MCP revision वाले जिन clients ने subscription stream खोली है, वे completion उसी पर पाते हैं; पुराने clients को वह unsolicited push मिलता है जिसकी इजाज़त पहले की spec देती थी। और 0.13.0 से, जो client देर से subscribe करता है — job के exit हो चुकने के बाद — उसे completion replay करके दे दी जाती है, बजाय इसके कि वह उस notification का हमेशा के लिए इंतज़ार करता रहे जो किसी के सुनने से पहले ही fire हो चुकी थी।
Foreground window भी 0.13.0 में तीस सेकंड से घटकर दस हो गई, और यह auto-promotion का अपनी क़ीमत वसूलना है: जब सीमा पार करने की कोई लागत नहीं — वही process, durable output, अंत में एक notification — तो session को आधे मिनट तक बंधक रखने की कोई वजह नहीं बचती, बस इस "क्या पता" में कि command पच्चीसवें सेकंड पर ख़त्म हो जाए।
तीसरा fix: jobs को एक-दूसरे के बग़ल में चलने दो
0.11.0 conservative था: एक directory में एक job, बस। सुरक्षित, और ज़रूरत से ज़्यादा भोथरा — उसने एक build और एक log tail को serialize कर दिया जिनका एक-दूसरे का इंतज़ार करने से कोई लेना-देना नहीं था।
0.14.0 ने guard को सिकोड़कर उस अकेले मामले तक सीमित कर दिया जो सचमुच बग है: उसी directory में पहले से चल रही हूबहू वही command। वह concurrency नहीं है, वह शुरुआती कहानी वाला दो बार दाग़ा गया test suite है, और उससे race करने के बजाय octofs उसे reject करता है और मॉडल को ठीक-ठीक बताता है कि क्या करना है:
The same shell command is already running as background job
octofs://jobs/17342-1 (`cargo test`). Wait for its completion — you will
get a resources/updated notification with its output — instead of
starting a duplicate. Independent commands may run concurrently in this
directory.
अलग-अलग commands साथ-साथ चलती हैं। Duplicate को एक ऐसी error मिलती है जो, एक बार फिर, ख़ुद ही recovery का निर्देश है।
और इस सब के नीचे एक सख़्त लकीर
जब इंतज़ार server कर रहा है, तो sleep 240 पर पूरी tool call फूँकने वाला मॉडल चालाक workaround नहीं रहा — वह शुद्ध बर्बादी बन गया। तो 0.10.4 ने उसे shell misuse list में जोड़ दिया, watch और top के बग़ल में:
Waiting with a bare `sleep` is forbidden — it burns the whole tool call
doing nothing.
To wait for a condition, poll it in a loop (sleep inside a loop body
is allowed):
until <check>; do sleep 2; done
To wait for a command you started, run it normally; long-running
commands automatically move to the background and notify you when
they finish.
वही नीति जो 0.9.0 के grep अस्वीकार की थी: hint मत दो, fail हो जाओ — और सही चाल error में रख दो। नंगे sleep को octofs reject करता है; until loop के अंदर का sleep एक जायज़ condition poll है और पास हो जाता है। watch और top को वह इसलिए reject करता है कि वे कभी exit नहीं करते — जिसका मतलब, event-driven shell में, यह है कि वे एक promotion slot हमेशा के लिए पकड़े रहते और completion कभी नहीं पहुँचाते।
सबके नीचे बहता धागा: hallucinate करने की कम जगहें
Shell के काम के इर्द-गिर्द पाँच छोटी releases 0.9.0 का धागा खींचती रहीं — उन दरारों को बंद करती हुईं जहाँ मॉडल ख़ामोशी या अस्पष्टता को जानकारी समझ सकता था।
ख़ाली search results खुलकर यही कहते हैं। जिस search को कुछ नहीं मिलता, वह, ख़ैर, बस कुछ नहीं भी लौटा सकती थी — और ख़ाली string थमाया गया मॉडल भरोसेमंद ढंग से "no matches" के नतीजे पर नहीं पहुँचता। कभी वह नतीजा निकालता है कि "tool fail हो गया" और retry करता है; कभी, इससे भी बुरा, वह ख़ामोशी को उससे भर देता है जो उसे मिलने की उम्मीद थी, और ऐसे आगे बढ़ता है मानो मिल गया हो। तो no-match का मामला एक वाक्य है, जो बताता है कि क्या खोजा गया और matches शून्य हैं — ऐसा व्यवहार जो 0.7 से चला आ रहा है, और जिसे 0.10.2 ने tests से इस तरह जकड़ दिया कि वह चुपचाप regress नहीं हो सकता। सबूत की ग़ैरहाज़िरी, ग़ैरहाज़िरी के सबूत के रूप में दर्ज।
Tool schemas ने अपने null variants छोड़ दिए (0.10.3)। JSON schema में optional-as-nullable इंसान को पढ़ने में ठीक लगता है और मॉडल के लिए एक attractive nuisance है — "path": null एक ऐसी call है जो validate होकर भी किसी अच्छी जगह नहीं पहुँचती। Optional का मतलब अब absent है।
पुराने पड़ चुके line IDs बेहतर रिपोर्ट होते हैं (0.10.5)। 0.9.0 की verification errors — जो ताज़ा content दिखाती हैं और बताती हैं कि आपका target कहाँ खिसका — दोनों ही बातों में ज़्यादा सटीक हो गईं।
Listing और search तेज़ हुए (0.10.1) — lossy UTF-8 conversion के बजाय raw bytes पर newline गिनती, हर entry पर फ़ालतू stat के बजाय directory walker से लिए गए file types, और एक whole-buffer prefilter जो line-level काम से पहले non-matching फ़ाइलें छाँट देता है। जिस tool को मॉडल एक session में सैकड़ों बार call करता है, उसकी latency हर चीज़ पर लगा tax है।
Remote मशीन, बिना तामझाम
Octofs 0.8.0 से SSH/SFTP बोलता है — किसी tool को ssh://user@host/path पर point करिए और एजेंट को remote मशीन पर वही verified filesystem मिल जाता है। दो releases ने यह काम पूरा किया।
0.14.0 targets को ~/.ssh/config से resolve करता है। Host aliases, एक ProxyJump bastion, IdentityFile, IdentityAgent, per-host users और ports — जो configuration आपने अपनी उँगलियों के आराम के लिए पहले से लिख रखी है, वह अब एजेंट के connections पर भी लागू होती है। परीक्षा सीधी है: अगर आपके terminal में सादा ssh box चलता है, तो octofs में ssh://box/path चलता है, bastion समेत। (ईमानदारी से कहें तो एक ही hop: multi-hop ProxyJump chains और ProxyCommand को हम आधे-अधूरे support के बजाय एक साफ़ error के साथ reject करते हैं।) इससे पहले एजेंट को वह पूरा खुलकर लिखा target चाहिए होता था जिससे बचाने के लिए ही आपकी config फ़ाइल मौजूद है।
0.14.1 ने misuse detection को ssh commands के अंदर पढ़ना सिखाया। 0.9.0 की grep-अस्वीकार वाली कहानी में एक remote के आकार का छेद था: ssh box 'grep -r TODO src/' उस detector के पास से बच निकलता था जो quotes का सम्मान इतनी शराफ़त से करता था कि उनके अंदर झाँकता ही नहीं था। अब वह remote command को SSH के options और nesting के आर-पार parse करता है और वही नियम लागू करता है — वही view path="ssh://box/src" content="TODO" जो local grep की जगह लेता है, remote वाले की भी लेता है। Pipelines पहले की तरह allowed हैं, interactive SSH अछूता है।
Handshake का client वाला सिरा
ऊपर की हर चीज़ एक protocol बातचीत का एक आधा हिस्सा है। दूसरा आधा वह है जो आपका agent runtime एक resource link और एक resources/updated notification के साथ करता है — और अगर वह कुछ नहीं करता, तो background jobs चुपचाप वापस polling में degrade हो जाती हैं।
हमारे एजेंट Octomind ने अपना आधा हिस्सा इन्हीं हफ़्तों में बनाया: background shell jobs एक असली lifecycle के साथ track होती हैं, context compaction से बच निकलती हैं (यही वह resource link है जो command का नाम साथ रखता है), और completions notification पहुँचते ही session में उतर जाती हैं। वह काम 0.47–0.48 cycle में फैला था और इसी हफ़्ते publish हुई Octomind 0.48.0 release post में cover हुआ है — उस release के बाक़ी हिस्से समेत, जो production code की लगभग दस हज़ार लाइनें net negative गई। अगर आप देखना चाहते हैं कि shell के block करना बंद कर देने पर एजेंट कैसा दिखता है: वह build शुरू करता है, build चलते-चलते अगली फ़ाइल एडिट करता है, और फ़ैसला तब पढ़ता है जब फ़ैसला मौजूद होता है।
वैसे octofs में कुछ भी Octomind पर निर्भर नहीं है। Job resources, links, notifications — सब सादा MCP है; protocol का पालन करने वाले किसी भी client को event-driven shell मुफ़्त में मिलता है।
अपग्रेड
# Homebrew
brew upgrade muvon/tap/octofs
# Cargo
cargo install octofs --version 0.14.1
# npm
npm install -g @muvon/octofs
Linux, macOS और Windows (x86_64 और ARM64) के लिए तैयार binaries releases पेज पर हैं।
किसी config बदलाव की ज़रूरत नहीं। एक behavioral बात ध्यान रखने की: अगर आपके prompts या client code shell tool को background flag पास करते थे, तो उसे हटा दें — flag जा चुका है और promotion अपने आप होता है। 0.9.0 में हटाए गए mode switches की तरह ही, इसकी जगह लेने को कुछ नहीं है; सही व्यवहार अब एकमात्र व्यवहार है।
फ़र्क़ पहली ही ऐसी command पर दिखता है जो दस सेकंड से लंबी खिंचती है। Blocked session, मारी गई call या duplicate run की जगह आपके एजेंट को उसकी बारी वापस मिलती है, चल रही job का link मिलता है, और एक notification तब मिलती है जब पढ़ने लायक कुछ हो।
Octofs ओपन सोर्स है (Apache 2.0), github.com/Muvon/octofs पर। 0.9.0 वाले लेख ने बताया था कि line number पर भरोसा क्यों नहीं किया जा सकता; इस लेख ने बताया कि blocked tool call पर भी क्यों नहीं। दोनों बार सिद्धांत एक ही है — server का काम मॉडल को कुछ ऐसा थमाना है जिस पर वह act कर सके, और "यहीं खड़े रहो जब तक कुछ नहीं होता" वह कभी नहीं था।



