nolo-cli 0.115.0-alpha.1 → 0.116.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{_executeModel-RQXGTKAT.js → _executeModel-BPUR2QYH.js} +15 -14
- package/{addDialogAgentAction-MCEABSBA.js → addDialogAgentAction-S2NRBCWW.js} +1 -1
- package/{agentDeleteCommand-S4GW4PKE.js → agentDeleteCommand-MNAA6MIG.js} +2 -2
- package/{agentEmailCommands-M26R6BO4.js → agentEmailCommands-SK34WJKU.js} +14 -14
- package/{agentListCommands-3YRR3FOM.js → agentListCommands-I26MKI4T.js} +2 -2
- package/{agentMachineCommands-ZFEQOZG3.js → agentMachineCommands-M6QDR6P3.js} +3 -3
- package/{agentRecordCommands-LZ4G5INZ.js → agentRecordCommands-HTHLO2MM.js} +14 -14
- package/{agentRunCommand-GOP4T77I.js → agentRunCommand-IAIRCMTH.js} +14 -14
- package/{agentRuntimeLocal-FXCNGFFT.js → agentRuntimeLocal-3WTYYIT2.js} +2 -2
- package/{agentSlice-PKMID4S2.js → agentSlice-TVP3HINW.js} +1 -1
- package/{appDeleteCommands-2LIWMMJP.js → appDeleteCommands-SB5TNLKY.js} +2 -2
- package/{appDeployCommands-WOTKY2LM.js → appDeployCommands-2IPKZINV.js} +2 -2
- package/{appDeployStatusCommands-6QQWIFCK.js → appDeployStatusCommands-K63TFUID.js} +2 -2
- package/{appGetCommands-Z4RHYUKS.js → appGetCommands-VU2SLNEH.js} +2 -2
- package/{appListCommands-SOBFAS4W.js → appListCommands-S3T4DJ36.js} +2 -2
- package/{chatQueueReduxAdapter-KMULSV7M.js → chatQueueReduxAdapter-TWIYKVIZ.js} +3 -3
- package/{chunk-2MMIOYAF.js → chunk-3BGIKIPF.js} +12 -12
- package/{chunk-SQCF7OWL.js → chunk-3IUWIAHP.js} +1 -1
- package/{chunk-SWM3OJR7.js → chunk-3VUWPRFW.js} +5 -4
- package/{chunk-RB3EETRZ.js → chunk-52Q2IW2X.js} +429 -53
- package/{chunk-TUBKLG3S.js → chunk-5MEYHVIB.js} +1 -1
- package/{chunk-6YTOJY35.js → chunk-6D2JFXLY.js} +35 -0
- package/{chunk-X767IQBD.js → chunk-DD6XE56O.js} +9 -62
- package/{chunk-KEQNY22P.js → chunk-FP76KI2Y.js} +54 -11
- package/{chunk-VLTIYV3X.js → chunk-HYJFRTLW.js} +1 -1
- package/{chunk-6P47Q5OH.js → chunk-KAUSRTO6.js} +2 -2
- package/{chunk-5F3OZ7L2.js → chunk-LMREEBWT.js} +35 -5
- package/{chunk-ANFQ4KPL.js → chunk-MZYBSTMH.js} +2 -2
- package/chunk-OCZKATRC.js +129 -0
- package/{chunk-22CGS5BQ.js → chunk-PMAIQX7A.js} +2 -2
- package/{chunk-NGEWETHC.js → chunk-RPJUFEGR.js} +3 -3
- package/{chunk-LZLZNZ4H.js → chunk-U4OKDZFH.js} +1 -1
- package/{chunk-DE7R7ZAP.js → chunk-UDVH2P4A.js} +3 -3
- package/{chunk-QTGQ2LTL.js → chunk-UEVXFHQB.js} +1 -1
- package/{chunk-Q4U2WKQY.js → chunk-XP3VVAPG.js} +90 -14
- package/{chunk-SBDFSHSS.js → chunk-Z2PV6O2T.js} +9 -0
- package/{contentThunks-EHSBUA4D.js → contentThunks-JYWI7G7I.js} +4 -4
- package/{createAgentAutomationAction-G2MK4LVL.js → createAgentAutomationAction-Z4SUJ54Y.js} +4 -4
- package/{createDialogAction-JH7ZCV5Z.js → createDialogAction-RCHFLVJ2.js} +5 -5
- package/{createPageAction-SRPNANEQ.js → createPageAction-YK2253XQ.js} +4 -4
- package/{dialogCommands-JCDHSPHY.js → dialogCommands-X4UOZQWX.js} +2 -2
- package/{dialogSlice-YUPGRKPA.js → dialogSlice-IVTWLCE2.js} +9 -3
- package/{docCreateCommands-HDV5VRAK.js → docCreateCommands-VD6TGHG4.js} +2 -2
- package/{docDeleteCommands-77BE7WA3.js → docDeleteCommands-OT3R2G4W.js} +2 -2
- package/{docReadCommands-4Z7DUAHQ.js → docReadCommands-2G7BIXSH.js} +2 -2
- package/{docStore-F3LR3M6Z.js → docStore-UENKCCDX.js} +1 -1
- package/{docUpdateCommands-L45KOOSF.js → docUpdateCommands-45MVJKZA.js} +2 -2
- package/{handleSendMessageAction-GCKW2H7N.js → handleSendMessageAction-LM2J4HC3.js} +40 -6
- package/index.js +41 -41
- package/{machineCommands-N3XF6SYH.js → machineCommands-6N6MQIE6.js} +14 -14
- package/{machineWsRunDispatch-UJX7MT2Z.js → machineWsRunDispatch-7MFKVQCR.js} +14 -14
- package/package.json +1 -1
- package/{readlineWorkspace-FFZEUHR4.js → readlineWorkspace-JWBON5CX.js} +34 -16
- package/{removeDialogAgentAction-J6X5QMTA.js → removeDialogAgentAction-W66BJ7CF.js} +1 -1
- package/{runAgentClientLoop-UGNX7QRL.js → runAgentClientLoop-TA4SSPHE.js} +12 -12
- package/{setDialogExtraReferencesAction-WX5VTY6O.js → setDialogExtraReferencesAction-2ALKG3WA.js} +2 -2
- package/{setPrimaryDialogAgentAction-FN22THHT.js → setPrimaryDialogAgentAction-TQZB55F6.js} +1 -1
- package/{spaceCommands-TCEN2CRN.js → spaceCommands-6NSAXB7D.js} +2 -2
- package/{streamAgentChatTurn-DKVJJ2BL.js → streamAgentChatTurn-X5ENGUIS.js} +76 -18
- package/{switchDialogAgentAction-LVTEV3FR.js → switchDialogAgentAction-3KI72HHS.js} +1 -1
- package/{toolApiClient-QVJ6HDNN.js → toolApiClient-G3M73RTB.js} +2 -2
- package/{tools-IHD7PL23.js → tools-XRQR7ZZR.js} +9 -9
- package/{updateDialogTitleAction-QKUFUNTO.js → updateDialogTitleAction-ESUUD3GT.js} +4 -4
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
requiresBareImageUrl,
|
|
51
51
|
resolveToolGuidedSections,
|
|
52
52
|
toBareImageUrlMessages
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-Z2PV6O2T.js";
|
|
54
54
|
import {
|
|
55
55
|
convertMessagesToResponsesInput,
|
|
56
56
|
extractReasoningFromResponseOutput,
|
|
@@ -11900,6 +11900,7 @@ var toQueue = /* @__PURE__ */ dual(2, (self, options) => toQueueArray(self.chann
|
|
|
11900
11900
|
var runIntoQueue = /* @__PURE__ */ dual(2, (self, queue) => runIntoQueueArray(self.channel, queue));
|
|
11901
11901
|
|
|
11902
11902
|
// packages/agent-runtime/abortableKernel.ts
|
|
11903
|
+
var IDLE_WATCHDOG_SLICES = 20;
|
|
11903
11904
|
function runAbortableWithTimeout(args2) {
|
|
11904
11905
|
const taskEffect = Effect_exports.callback(
|
|
11905
11906
|
(resume) => {
|
|
@@ -11935,12 +11936,26 @@ function runAbortableWithTimeout(args2) {
|
|
|
11935
11936
|
raced = Effect_exports.raceFirst(taskEffect, abortEffect);
|
|
11936
11937
|
}
|
|
11937
11938
|
if (typeof args2.timeoutMs === "number") {
|
|
11938
|
-
const
|
|
11939
|
+
const outer = raced;
|
|
11939
11940
|
const timeoutMs = args2.timeoutMs;
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11941
|
+
const activityVersion = args2.activityVersion;
|
|
11942
|
+
const idleWatchdog = Effect_exports.gen(function* () {
|
|
11943
|
+
const sliceCount = activityVersion ? IDLE_WATCHDOG_SLICES : 1;
|
|
11944
|
+
const baseSliceMs = Math.max(1, Math.floor(timeoutMs / sliceCount));
|
|
11945
|
+
let idleMs = 0;
|
|
11946
|
+
for (; ; ) {
|
|
11947
|
+
const versionAtSliceStart = activityVersion?.();
|
|
11948
|
+
const sliceMs = Math.max(1, Math.min(timeoutMs - idleMs, baseSliceMs));
|
|
11949
|
+
yield* Effect_exports.sleep(Duration_exports.millis(sliceMs));
|
|
11950
|
+
idleMs += sliceMs;
|
|
11951
|
+
if (activityVersion && activityVersion() !== versionAtSliceStart) {
|
|
11952
|
+
idleMs = 0;
|
|
11953
|
+
continue;
|
|
11954
|
+
}
|
|
11955
|
+
if (idleMs >= timeoutMs) return { kind: "timeout" };
|
|
11956
|
+
}
|
|
11943
11957
|
});
|
|
11958
|
+
raced = Effect_exports.raceFirst(outer, idleWatchdog);
|
|
11944
11959
|
}
|
|
11945
11960
|
return args2.runtime ? args2.runtime.runPromise(raced) : Effect_exports.runPromise(raced);
|
|
11946
11961
|
}
|
|
@@ -12627,6 +12642,13 @@ function isColdResume(history, nowMs) {
|
|
|
12627
12642
|
}
|
|
12628
12643
|
async function maybeAutoCompactLocalHistory(args2) {
|
|
12629
12644
|
const { adapter, dialogId, history } = args2;
|
|
12645
|
+
const emitPhase = (phase) => {
|
|
12646
|
+
try {
|
|
12647
|
+
args2.onPhase?.(phase);
|
|
12648
|
+
} catch {
|
|
12649
|
+
}
|
|
12650
|
+
};
|
|
12651
|
+
emitPhase({ kind: "validating-context" });
|
|
12630
12652
|
const unchanged = (decision) => ({
|
|
12631
12653
|
history,
|
|
12632
12654
|
compressed: false,
|
|
@@ -12722,8 +12744,15 @@ async function maybeAutoCompactLocalHistory(args2) {
|
|
|
12722
12744
|
if (!plan.shouldCompress) {
|
|
12723
12745
|
return projectExisting();
|
|
12724
12746
|
}
|
|
12747
|
+
emitPhase({ kind: "compaction-start" });
|
|
12725
12748
|
try {
|
|
12749
|
+
if (args2.abortSignal?.aborted) {
|
|
12750
|
+
throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
|
|
12751
|
+
}
|
|
12726
12752
|
const provider = await args2.resolveProvider();
|
|
12753
|
+
if (args2.abortSignal?.aborted) {
|
|
12754
|
+
throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
|
|
12755
|
+
}
|
|
12727
12756
|
const msgsToCompress = plan.msgsToCompress;
|
|
12728
12757
|
const messagesText = formatMessagesForSummaryWithTruncation(msgsToCompress);
|
|
12729
12758
|
const fileOpsText = formatFileOperationsFromMessages(
|
|
@@ -12735,20 +12764,55 @@ async function maybeAutoCompactLocalHistory(args2) {
|
|
|
12735
12764
|
messagesText,
|
|
12736
12765
|
fileOpsText
|
|
12737
12766
|
});
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12767
|
+
emitPhase({ kind: "waiting-provider" });
|
|
12768
|
+
const timeoutMs = typeof args2.timeoutMs === "number" && Number.isFinite(args2.timeoutMs) && args2.timeoutMs > 0 ? args2.timeoutMs : void 0;
|
|
12769
|
+
const requestController = new AbortController();
|
|
12770
|
+
let timeoutHandle;
|
|
12771
|
+
let abortListener;
|
|
12772
|
+
const request3 = provider.complete(
|
|
12773
|
+
[{ role: "system", content: COMPACTION_SUMMARY_SYSTEM_PROMPT }, { role: "user", content: promptContent }],
|
|
12774
|
+
{ signal: requestController.signal, ...timeoutMs ? { timeoutMs } : {} }
|
|
12775
|
+
).then(
|
|
12776
|
+
(value) => ({ kind: "done", value }),
|
|
12777
|
+
(error) => ({ kind: "provider-error", error })
|
|
12778
|
+
);
|
|
12779
|
+
const guards = [];
|
|
12780
|
+
if (timeoutMs) guards.push(new Promise((resolve5) => {
|
|
12781
|
+
timeoutHandle = setTimeout(() => {
|
|
12782
|
+
requestController.abort();
|
|
12783
|
+
resolve5({ kind: "timeout" });
|
|
12784
|
+
}, timeoutMs);
|
|
12785
|
+
}));
|
|
12786
|
+
if (args2.abortSignal) guards.push(new Promise((resolve5) => {
|
|
12787
|
+
abortListener = () => {
|
|
12788
|
+
requestController.abort(args2.abortSignal?.reason);
|
|
12789
|
+
resolve5({ kind: "aborted" });
|
|
12790
|
+
};
|
|
12791
|
+
args2.abortSignal?.addEventListener("abort", abortListener, { once: true });
|
|
12792
|
+
}));
|
|
12793
|
+
const outcome = guards.length ? await Promise.race([request3, ...guards]) : await request3;
|
|
12794
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
12795
|
+
if (abortListener) args2.abortSignal?.removeEventListener("abort", abortListener);
|
|
12796
|
+
if (outcome.kind !== "done") {
|
|
12797
|
+
const reason = outcome.kind === "timeout" ? "timeout" : outcome.kind === "aborted" ? "aborted" : "provider-error";
|
|
12798
|
+
const detail = outcome.kind === "provider-error" ? outcome.error instanceof Error ? outcome.error.message : String(outcome.error) : reason === "timeout" ? `auto-compaction timed out after ${timeoutMs}ms` : "auto-compaction aborted";
|
|
12799
|
+
throw Object.assign(new Error(detail), { compactionReason: reason });
|
|
12800
|
+
}
|
|
12801
|
+
const result4 = outcome.value;
|
|
12742
12802
|
const newSummary = typeof result4.content === "string" ? result4.content.trim() : "";
|
|
12743
12803
|
if (!newSummary) {
|
|
12744
12804
|
console.warn(
|
|
12745
12805
|
"[localLoop] auto-compaction produced empty summary; keeping prior projection"
|
|
12746
12806
|
);
|
|
12807
|
+
const failureReason = "provider-error";
|
|
12808
|
+
emitPhase({ kind: "compaction-failed", reason: failureReason });
|
|
12747
12809
|
return {
|
|
12748
12810
|
...projectExisting(),
|
|
12749
12811
|
failureMessage: "summary model returned empty content",
|
|
12812
|
+
failureReason,
|
|
12750
12813
|
decision: {
|
|
12751
12814
|
outcome: "failed",
|
|
12815
|
+
reason: failureReason,
|
|
12752
12816
|
detail: "summary model returned empty content"
|
|
12753
12817
|
}
|
|
12754
12818
|
};
|
|
@@ -12775,6 +12839,7 @@ async function maybeAutoCompactLocalHistory(args2) {
|
|
|
12775
12839
|
summaryUsage: result4.usage
|
|
12776
12840
|
});
|
|
12777
12841
|
console.log(formatCompactionMetricsLog(metrics));
|
|
12842
|
+
emitPhase({ kind: "compaction-end" });
|
|
12778
12843
|
return {
|
|
12779
12844
|
history: projectHistoryWithSummary({
|
|
12780
12845
|
history,
|
|
@@ -12802,10 +12867,13 @@ async function maybeAutoCompactLocalHistory(args2) {
|
|
|
12802
12867
|
} catch (error) {
|
|
12803
12868
|
console.warn("[localLoop] auto-compaction failed:", error);
|
|
12804
12869
|
const failureMessage = error instanceof Error && error.message ? error.message.slice(0, 200) : String(error).slice(0, 200);
|
|
12870
|
+
const failureReason = error instanceof Error && "compactionReason" in error ? error.compactionReason : "provider-error";
|
|
12871
|
+
emitPhase({ kind: "compaction-failed", reason: failureReason });
|
|
12805
12872
|
return {
|
|
12806
12873
|
...projectExisting(),
|
|
12807
12874
|
failureMessage,
|
|
12808
|
-
|
|
12875
|
+
failureReason,
|
|
12876
|
+
decision: { outcome: "failed", reason: failureReason, detail: failureMessage }
|
|
12809
12877
|
};
|
|
12810
12878
|
}
|
|
12811
12879
|
}
|
|
@@ -12862,6 +12930,8 @@ function emitLoopEvent(boundary, event, bridge) {
|
|
|
12862
12930
|
boundary.emit(bridge ? { event, bridge } : { event });
|
|
12863
12931
|
}
|
|
12864
12932
|
var LLM_REQUEST_TIMEOUT = "LLM_REQUEST_TIMEOUT";
|
|
12933
|
+
var DEFAULT_LLM_REQUEST_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
12934
|
+
var DEFAULT_COMPACTION_TIMEOUT_MS = 60 * 1e3;
|
|
12865
12935
|
var LOCAL_TURN_ABORTED_CODE = "LOCAL_TURN_ABORTED";
|
|
12866
12936
|
function buildAbortedError() {
|
|
12867
12937
|
const error = new Error("local agent turn aborted by user");
|
|
@@ -12889,8 +12959,11 @@ async function runAbortableToolTask(input, task, pendingToolName) {
|
|
|
12889
12959
|
}
|
|
12890
12960
|
function resolveLlmRequestTimeoutMs(input) {
|
|
12891
12961
|
const raw = input.llmRequestTimeoutMs ?? input.timeoutMs;
|
|
12892
|
-
|
|
12893
|
-
|
|
12962
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_LLM_REQUEST_TIMEOUT_MS;
|
|
12963
|
+
}
|
|
12964
|
+
function resolveCompactionTimeoutMs(input) {
|
|
12965
|
+
const raw = input.compactionTimeoutMs;
|
|
12966
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_COMPACTION_TIMEOUT_MS;
|
|
12894
12967
|
}
|
|
12895
12968
|
async function runCompleteWithTimeout(args2) {
|
|
12896
12969
|
const { provider, messages, options, timeoutMs, round, input, boundary, context: context3, providerName, model } = args2;
|
|
@@ -12913,6 +12986,7 @@ async function runCompleteWithTimeout(args2) {
|
|
|
12913
12986
|
const outcome = await runAbortableWithTimeout({
|
|
12914
12987
|
task: complete,
|
|
12915
12988
|
timeoutMs: typeof timeoutMs === "number" ? timeoutMs : void 0,
|
|
12989
|
+
activityVersion: args2.activityVersion,
|
|
12916
12990
|
abortSignal: input.abortSignal,
|
|
12917
12991
|
runtime: input.effectRuntime
|
|
12918
12992
|
});
|
|
@@ -12923,7 +12997,7 @@ async function runCompleteWithTimeout(args2) {
|
|
|
12923
12997
|
}
|
|
12924
12998
|
if (outcome.kind === "timeout") {
|
|
12925
12999
|
const timeoutError = new Error(
|
|
12926
|
-
`LLM request timed out after ${timeoutMs}ms (round ${round})`
|
|
13000
|
+
`LLM request timed out after ${timeoutMs}ms without new output (idle window, round ${round})`
|
|
12927
13001
|
);
|
|
12928
13002
|
timeoutError.code = LLM_REQUEST_TIMEOUT;
|
|
12929
13003
|
throw timeoutError;
|
|
@@ -13639,6 +13713,16 @@ async function runLocalAgentTurn(input) {
|
|
|
13639
13713
|
}
|
|
13640
13714
|
}
|
|
13641
13715
|
let compactionUsage;
|
|
13716
|
+
const emitCompactionPhase = (phase, scope3) => {
|
|
13717
|
+
const atMs = Date.now();
|
|
13718
|
+
if (phase.kind === "validating-context") emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "validating-context", atMs, compactionScope: scope3 });
|
|
13719
|
+
else if (phase.kind === "compaction-start") {
|
|
13720
|
+
emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "compacting", atMs, compactionScope: scope3 });
|
|
13721
|
+
emitLoopEvent(observationBoundary, { kind: "compaction-start", atMs, scope: scope3 });
|
|
13722
|
+
} else if (phase.kind === "waiting-provider") emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "waiting-provider", atMs, compactionScope: scope3 });
|
|
13723
|
+
else if (phase.kind === "compaction-end") emitLoopEvent(observationBoundary, { kind: "compaction-end", atMs, scope: scope3 });
|
|
13724
|
+
else emitLoopEvent(observationBoundary, { kind: "compaction-failed", atMs, scope: scope3, reason: phase.reason });
|
|
13725
|
+
};
|
|
13642
13726
|
const emitCompactionObservation = (compacted) => {
|
|
13643
13727
|
const decision = compacted.decision;
|
|
13644
13728
|
if (decision?.outcome === "skipped") {
|
|
@@ -13672,7 +13756,10 @@ async function runLocalAgentTurn(input) {
|
|
|
13672
13756
|
model: agentConfig.model,
|
|
13673
13757
|
resolveProvider: resolveProviderOnce,
|
|
13674
13758
|
contextWindow,
|
|
13675
|
-
realContextUsagePercent
|
|
13759
|
+
realContextUsagePercent,
|
|
13760
|
+
abortSignal: input.abortSignal,
|
|
13761
|
+
timeoutMs: resolveCompactionTimeoutMs(input),
|
|
13762
|
+
onPhase: (phase) => emitCompactionPhase(phase, "initial")
|
|
13676
13763
|
});
|
|
13677
13764
|
history = compacted.history;
|
|
13678
13765
|
compactionUsage = compacted.usage;
|
|
@@ -13778,7 +13865,10 @@ async function runLocalAgentTurn(input) {
|
|
|
13778
13865
|
model: agentConfig.model,
|
|
13779
13866
|
resolveProvider: resolveProviderOnce,
|
|
13780
13867
|
contextWindow,
|
|
13781
|
-
...ratio !== void 0 ? { realContextUsagePercent: ratio } : {}
|
|
13868
|
+
...ratio !== void 0 ? { realContextUsagePercent: ratio } : {},
|
|
13869
|
+
abortSignal: input.abortSignal,
|
|
13870
|
+
timeoutMs: resolveCompactionTimeoutMs(input),
|
|
13871
|
+
onPhase: (phase) => emitCompactionPhase(phase, "in-loop")
|
|
13782
13872
|
});
|
|
13783
13873
|
if (compacted.usage) {
|
|
13784
13874
|
compactionUsage = addOutOfBandUsage(compactionUsage, compacted.usage);
|
|
@@ -13848,11 +13938,12 @@ async function runLocalAgentTurn(input) {
|
|
|
13848
13938
|
const shouldPassToolEvents = Boolean(
|
|
13849
13939
|
input.onToolEvent || input.onObservationEvent || input.observationBoundary
|
|
13850
13940
|
);
|
|
13941
|
+
let llmStreamActivity = 0;
|
|
13851
13942
|
result4 = await runCompleteWithTimeout({
|
|
13852
13943
|
provider,
|
|
13853
13944
|
messages: requestMessages,
|
|
13854
13945
|
options: {
|
|
13855
|
-
|
|
13946
|
+
timeoutMs: resolveLlmRequestTimeoutMs(input),
|
|
13856
13947
|
// 用户取消信号穿进 provider options:providerStreamRetry 据此在
|
|
13857
13948
|
// 取消后跳过重试,provider 分支(如 antigravity/openai-compatible)
|
|
13858
13949
|
// 也可把 fetch 绑定到同一信号,实现真正的传输层取消。
|
|
@@ -13861,6 +13952,7 @@ async function runLocalAgentTurn(input) {
|
|
|
13861
13952
|
// 归到具体对话而不是 chat-proxy 兜底桶。新对话首轮 id 尚未分配。
|
|
13862
13953
|
...input.continueDialogId ? { dialogId: input.continueDialogId } : {},
|
|
13863
13954
|
...shouldStreamDeltas ? { onTextDelta: (chunk) => {
|
|
13955
|
+
llmStreamActivity += 1;
|
|
13864
13956
|
partialContent += chunk;
|
|
13865
13957
|
observationBoundary.emit({
|
|
13866
13958
|
kind: "text-delta",
|
|
@@ -13870,6 +13962,7 @@ async function runLocalAgentTurn(input) {
|
|
|
13870
13962
|
});
|
|
13871
13963
|
} } : {},
|
|
13872
13964
|
...shouldStreamReasoning ? { onReasoningDelta: (chunk) => {
|
|
13965
|
+
llmStreamActivity += 1;
|
|
13873
13966
|
observationBoundary.emit({
|
|
13874
13967
|
kind: "reasoning-delta",
|
|
13875
13968
|
chunk,
|
|
@@ -13878,6 +13971,7 @@ async function runLocalAgentTurn(input) {
|
|
|
13878
13971
|
});
|
|
13879
13972
|
} } : {},
|
|
13880
13973
|
...shouldPassToolEvents ? { onToolEvent: (event) => {
|
|
13974
|
+
llmStreamActivity += 1;
|
|
13881
13975
|
observationBoundary.emit({
|
|
13882
13976
|
kind: "tool-event",
|
|
13883
13977
|
event,
|
|
@@ -13888,6 +13982,7 @@ async function runLocalAgentTurn(input) {
|
|
|
13888
13982
|
...shouldPassToolEvents ? { toolEventRound: round } : {}
|
|
13889
13983
|
},
|
|
13890
13984
|
timeoutMs: resolveLlmRequestTimeoutMs(input),
|
|
13985
|
+
activityVersion: () => llmStreamActivity,
|
|
13891
13986
|
round,
|
|
13892
13987
|
input,
|
|
13893
13988
|
boundary: observationBoundary,
|
|
@@ -16436,29 +16531,6 @@ function getProcessRegistry() {
|
|
|
16436
16531
|
return registry;
|
|
16437
16532
|
}
|
|
16438
16533
|
|
|
16439
|
-
// packages/agent-runtime/processOwnership.ts
|
|
16440
|
-
var asNonEmptyString = (value) => {
|
|
16441
|
-
if (typeof value !== "string") return null;
|
|
16442
|
-
const trimmed = value.trim();
|
|
16443
|
-
return trimmed ? trimmed : null;
|
|
16444
|
-
};
|
|
16445
|
-
function readProcessOwner(runtimeContext) {
|
|
16446
|
-
if (!runtimeContext || typeof runtimeContext !== "object") return null;
|
|
16447
|
-
const dialogId = asNonEmptyString(runtimeContext.dialogId) ?? asNonEmptyString(runtimeContext.parentThreadId);
|
|
16448
|
-
if (!dialogId) return null;
|
|
16449
|
-
const turnId = asNonEmptyString(runtimeContext.turnId);
|
|
16450
|
-
const ownerId = asNonEmptyString(runtimeContext.ownerId);
|
|
16451
|
-
return {
|
|
16452
|
-
dialogId,
|
|
16453
|
-
...turnId ? { turnId } : {},
|
|
16454
|
-
...ownerId ? { ownerId } : {}
|
|
16455
|
-
};
|
|
16456
|
-
}
|
|
16457
|
-
|
|
16458
|
-
// packages/agent-runtime/workspaceShell.ts
|
|
16459
|
-
import { resolve as resolve3 } from "node:path";
|
|
16460
|
-
import { spawn as spawnChildProcess2 } from "node:child_process";
|
|
16461
|
-
|
|
16462
16534
|
// packages/agent-runtime/processTaskResult.ts
|
|
16463
16535
|
var PROCESS_RESULT_CAPSULE_TAIL_CHARS = 4e3;
|
|
16464
16536
|
var PROCESS_RESULT_CAPSULE_SPILL_THRESHOLD_CHARS = 2e4;
|
|
@@ -16478,11 +16550,17 @@ function buildProcessTaskResultCapsule(args2) {
|
|
|
16478
16550
|
const exitCode = args2.exitCode;
|
|
16479
16551
|
const captureError = args2.captureError?.trim() || void 0;
|
|
16480
16552
|
const durationMs = typeof args2.startedAt === "number" && Number.isFinite(args2.startedAt) ? Math.max(0, (args2.endedAt ?? Date.now()) - args2.startedAt) : void 0;
|
|
16481
|
-
const
|
|
16482
|
-
const
|
|
16553
|
+
const stdoutProvenance = args2.streamProvenance?.stdout;
|
|
16554
|
+
const stderrProvenance = args2.streamProvenance?.stderr;
|
|
16555
|
+
const ringEvicted = Boolean(stdoutProvenance?.evicted || stderrProvenance?.evicted);
|
|
16556
|
+
const stdoutTailSource = typeof args2.stdoutTail === "string" ? args2.stdoutTail : stdout;
|
|
16557
|
+
const stderrTailSource = typeof args2.stderrTail === "string" ? args2.stderrTail : stderr;
|
|
16558
|
+
const stdoutTail = tailText(stdoutTailSource, tailChars);
|
|
16559
|
+
const stderrTail = tailText(stderrTailSource, tailChars);
|
|
16483
16560
|
const inlineSize = stdout.length + stderr.length;
|
|
16484
16561
|
const inlineTruncated = stdoutTail.truncated || stderrTail.truncated;
|
|
16485
|
-
const
|
|
16562
|
+
const truncated = inlineTruncated || ringEvicted || Boolean(captureError);
|
|
16563
|
+
const shouldSpill = truncated || inlineSize > spillThresholdChars;
|
|
16486
16564
|
let spill;
|
|
16487
16565
|
if (shouldSpill) {
|
|
16488
16566
|
try {
|
|
@@ -16496,8 +16574,11 @@ function buildProcessTaskResultCapsule(args2) {
|
|
|
16496
16574
|
});
|
|
16497
16575
|
spill = {
|
|
16498
16576
|
displayPath: result4.displayPath,
|
|
16499
|
-
|
|
16500
|
-
|
|
16577
|
+
// Prefer the caller's live provenance counters: they count every
|
|
16578
|
+
// byte of the original stream, while the buffered content above may
|
|
16579
|
+
// be a capped prefix of it.
|
|
16580
|
+
totalChars: (stdoutProvenance?.totalChars ?? stdout.length) + (stderrProvenance?.totalChars ?? stderr.length),
|
|
16581
|
+
totalLines: (stdoutProvenance?.totalLines ?? countLines(stdout)) + (stderrProvenance?.totalLines ?? countLines(stderr))
|
|
16501
16582
|
};
|
|
16502
16583
|
} catch {
|
|
16503
16584
|
}
|
|
@@ -16508,12 +16589,33 @@ function buildProcessTaskResultCapsule(args2) {
|
|
|
16508
16589
|
exitCode,
|
|
16509
16590
|
...durationMs !== void 0 ? { durationMs } : {},
|
|
16510
16591
|
...captureError ? { captureError } : {},
|
|
16511
|
-
truncated
|
|
16592
|
+
truncated,
|
|
16512
16593
|
...spill ? { spill } : {}
|
|
16513
16594
|
};
|
|
16514
16595
|
}
|
|
16515
16596
|
|
|
16597
|
+
// packages/agent-runtime/processOwnership.ts
|
|
16598
|
+
var asNonEmptyString = (value) => {
|
|
16599
|
+
if (typeof value !== "string") return null;
|
|
16600
|
+
const trimmed = value.trim();
|
|
16601
|
+
return trimmed ? trimmed : null;
|
|
16602
|
+
};
|
|
16603
|
+
function readProcessOwner(runtimeContext) {
|
|
16604
|
+
if (!runtimeContext || typeof runtimeContext !== "object") return null;
|
|
16605
|
+
const dialogId = asNonEmptyString(runtimeContext.dialogId) ?? asNonEmptyString(runtimeContext.parentThreadId);
|
|
16606
|
+
if (!dialogId) return null;
|
|
16607
|
+
const turnId = asNonEmptyString(runtimeContext.turnId);
|
|
16608
|
+
const ownerId = asNonEmptyString(runtimeContext.ownerId);
|
|
16609
|
+
return {
|
|
16610
|
+
dialogId,
|
|
16611
|
+
...turnId ? { turnId } : {},
|
|
16612
|
+
...ownerId ? { ownerId } : {}
|
|
16613
|
+
};
|
|
16614
|
+
}
|
|
16615
|
+
|
|
16516
16616
|
// packages/agent-runtime/workspaceShell.ts
|
|
16617
|
+
import { resolve as resolve3 } from "node:path";
|
|
16618
|
+
import { spawn as spawnChildProcess2 } from "node:child_process";
|
|
16517
16619
|
var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
|
|
16518
16620
|
var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
|
|
16519
16621
|
var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
|
|
@@ -17382,13 +17484,169 @@ function runTasks(args2) {
|
|
|
17382
17484
|
return { tasks, count: tasks.length };
|
|
17383
17485
|
}
|
|
17384
17486
|
|
|
17487
|
+
// packages/agent-runtime/execShellCompletionWarnings.ts
|
|
17488
|
+
var BG_ESCAPE_WORD_PATTERN = /(?:^|[\s;|(&/\\])(nohup|disown|setsid|start-process)(?=$|[\s;|)&])/i;
|
|
17489
|
+
var SHELL_SPLIT_CHARS = /* @__PURE__ */ new Set([";", "|", "&", "(", ")", "<", ">", "\n", " ", " "]);
|
|
17490
|
+
function maskQuotedAndEscaped(command) {
|
|
17491
|
+
const out = Array.from(command);
|
|
17492
|
+
let quote = null;
|
|
17493
|
+
for (let i = 0; i < out.length; i += 1) {
|
|
17494
|
+
const ch = out[i];
|
|
17495
|
+
if (quote) {
|
|
17496
|
+
out[i] = " ";
|
|
17497
|
+
if (ch === "\\" && quote !== "'") {
|
|
17498
|
+
if (i + 1 < out.length) out[i + 1] = " ";
|
|
17499
|
+
i += 1;
|
|
17500
|
+
} else if (ch === quote) {
|
|
17501
|
+
quote = null;
|
|
17502
|
+
}
|
|
17503
|
+
continue;
|
|
17504
|
+
}
|
|
17505
|
+
if (ch === "\\") {
|
|
17506
|
+
out[i] = " ";
|
|
17507
|
+
if (i + 1 < out.length) out[i + 1] = " ";
|
|
17508
|
+
i += 1;
|
|
17509
|
+
continue;
|
|
17510
|
+
}
|
|
17511
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
17512
|
+
quote = ch;
|
|
17513
|
+
out[i] = " ";
|
|
17514
|
+
continue;
|
|
17515
|
+
}
|
|
17516
|
+
if (ch === "#" && (i === 0 || /[\s;]/.test(out[i - 1] ?? " "))) {
|
|
17517
|
+
while (i < out.length && out[i] !== "\n") {
|
|
17518
|
+
out[i] = " ";
|
|
17519
|
+
i += 1;
|
|
17520
|
+
}
|
|
17521
|
+
}
|
|
17522
|
+
}
|
|
17523
|
+
return out.join("");
|
|
17524
|
+
}
|
|
17525
|
+
function readRedirectTarget(original, start) {
|
|
17526
|
+
let i = start;
|
|
17527
|
+
while (i < original.length && /\s/.test(original[i] ?? "")) {
|
|
17528
|
+
i += 1;
|
|
17529
|
+
}
|
|
17530
|
+
if (i >= original.length) return void 0;
|
|
17531
|
+
let text = "";
|
|
17532
|
+
let j = i;
|
|
17533
|
+
while (j < original.length) {
|
|
17534
|
+
const o = original[j];
|
|
17535
|
+
if (o === "'" || o === '"' || o === "`") {
|
|
17536
|
+
const quote = o;
|
|
17537
|
+
j += 1;
|
|
17538
|
+
while (j < original.length && original[j] !== quote) {
|
|
17539
|
+
if (original[j] === "\\" && quote !== "'" && j + 1 < original.length) {
|
|
17540
|
+
text += original[j + 1];
|
|
17541
|
+
j += 2;
|
|
17542
|
+
continue;
|
|
17543
|
+
}
|
|
17544
|
+
text += original[j];
|
|
17545
|
+
j += 1;
|
|
17546
|
+
}
|
|
17547
|
+
j += 1;
|
|
17548
|
+
continue;
|
|
17549
|
+
}
|
|
17550
|
+
if (/\s/.test(o) || SHELL_SPLIT_CHARS.has(o)) break;
|
|
17551
|
+
text += o;
|
|
17552
|
+
j += 1;
|
|
17553
|
+
}
|
|
17554
|
+
if (!text || text === "$") return void 0;
|
|
17555
|
+
return { text, end: j };
|
|
17556
|
+
}
|
|
17557
|
+
function isIgnoredRedirectTarget(target) {
|
|
17558
|
+
return target === "/dev/null";
|
|
17559
|
+
}
|
|
17560
|
+
function detectExecShellSynchronousWarnings(command, shell) {
|
|
17561
|
+
const masked = maskQuotedAndEscaped(command);
|
|
17562
|
+
const isPowerShell = shell?.resolvedShell === "powershell";
|
|
17563
|
+
const redirectTargets = [];
|
|
17564
|
+
let backgrounded = false;
|
|
17565
|
+
let i = 0;
|
|
17566
|
+
while (i < masked.length) {
|
|
17567
|
+
const ch = masked[i];
|
|
17568
|
+
if (ch === "&") {
|
|
17569
|
+
const next = masked[i + 1];
|
|
17570
|
+
if (next === "&") {
|
|
17571
|
+
i += 2;
|
|
17572
|
+
continue;
|
|
17573
|
+
}
|
|
17574
|
+
if (next === ">") {
|
|
17575
|
+
let j = i + 2;
|
|
17576
|
+
if (masked[j] === ">") j += 1;
|
|
17577
|
+
const target = readRedirectTarget(command, j);
|
|
17578
|
+
if (target && !isIgnoredRedirectTarget(target.text)) redirectTargets.push(target.text);
|
|
17579
|
+
i = target ? target.end : j;
|
|
17580
|
+
continue;
|
|
17581
|
+
}
|
|
17582
|
+
const prev = masked[i - 1];
|
|
17583
|
+
if (prev === ">" || prev === "<") {
|
|
17584
|
+
i += 1;
|
|
17585
|
+
continue;
|
|
17586
|
+
}
|
|
17587
|
+
if (isPowerShell) {
|
|
17588
|
+
i += 1;
|
|
17589
|
+
continue;
|
|
17590
|
+
}
|
|
17591
|
+
backgrounded = true;
|
|
17592
|
+
i += 1;
|
|
17593
|
+
continue;
|
|
17594
|
+
}
|
|
17595
|
+
if (ch === ">") {
|
|
17596
|
+
const prev = masked[i - 1];
|
|
17597
|
+
if (prev === ">" || prev === "&" || prev === "<") {
|
|
17598
|
+
i += 1;
|
|
17599
|
+
continue;
|
|
17600
|
+
}
|
|
17601
|
+
let j = i + 1;
|
|
17602
|
+
if (masked[j] === ">") j += 1;
|
|
17603
|
+
if (masked[j] === "&") {
|
|
17604
|
+
const ampTarget = readRedirectTarget(command, j + 1);
|
|
17605
|
+
const isFdDuplication = ampTarget !== void 0 && (/^\d+$/.test(ampTarget.text) || ampTarget.text === "-");
|
|
17606
|
+
if (ampTarget && !isFdDuplication && !isIgnoredRedirectTarget(ampTarget.text)) {
|
|
17607
|
+
redirectTargets.push(ampTarget.text);
|
|
17608
|
+
}
|
|
17609
|
+
i = ampTarget ? ampTarget.end : j + 1;
|
|
17610
|
+
continue;
|
|
17611
|
+
}
|
|
17612
|
+
const target = readRedirectTarget(command, j);
|
|
17613
|
+
if (target && !isIgnoredRedirectTarget(target.text)) redirectTargets.push(target.text);
|
|
17614
|
+
i = target ? target.end : j;
|
|
17615
|
+
continue;
|
|
17616
|
+
}
|
|
17617
|
+
if (ch === "<") {
|
|
17618
|
+
i += masked[i + 1] === "<" || masked[i + 1] === "&" ? 2 : 1;
|
|
17619
|
+
continue;
|
|
17620
|
+
}
|
|
17621
|
+
i += 1;
|
|
17622
|
+
}
|
|
17623
|
+
const wordMatch = BG_ESCAPE_WORD_PATTERN.exec(masked);
|
|
17624
|
+
if (wordMatch) backgrounded = true;
|
|
17625
|
+
const warnings = [];
|
|
17626
|
+
if (redirectTargets.length > 0) {
|
|
17627
|
+
const target = redirectTargets[0];
|
|
17628
|
+
warnings.push({
|
|
17629
|
+
kind: "output-redirect",
|
|
17630
|
+
redirectTarget: target,
|
|
17631
|
+
message: `[wake-safety] This execShell call completed synchronously; its output was redirected to ${target}. That file is NOT delivered by any completion notification \u2014 no tracked background task was created. If you need the result, read that file in this turn.`
|
|
17632
|
+
});
|
|
17633
|
+
}
|
|
17634
|
+
if (backgrounded) {
|
|
17635
|
+
warnings.push({
|
|
17636
|
+
kind: "shell-backgrounding",
|
|
17637
|
+
message: `[wake-safety] Detected shell-internal backgrounding (lone \`&\`, nohup/disown/setsid/Start-Process or similar). Descendant processes are NOT tracked as platform tasks and will not produce completion notifications; text such as \`LAUNCHED\` printed by the command is plain stdout, not a platform receipt.`
|
|
17638
|
+
});
|
|
17639
|
+
}
|
|
17640
|
+
return warnings;
|
|
17641
|
+
}
|
|
17642
|
+
|
|
17385
17643
|
// packages/agent-runtime/capabilities/execShellCapability.ts
|
|
17386
17644
|
function buildExecShellToolDefinition(toolName = "execShell") {
|
|
17387
17645
|
return {
|
|
17388
17646
|
type: "function",
|
|
17389
17647
|
function: {
|
|
17390
17648
|
name: toolName,
|
|
17391
|
-
description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess. On Windows the resolved shell may be Windows PowerShell 5.1 (metadata.shellKind="powershell5"): it does not support '&&'/'||' or $PSStyle \u2014 join commands with ';' or split into multiple calls. metadata.shellKind="pwsh" means PowerShell 7+ with full syntax. Read resolvedShell/shellKind from any execShell result before writing Windows-specific syntax.`,
|
|
17649
|
+
description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess. On Windows the resolved shell may be Windows PowerShell 5.1 (metadata.shellKind="powershell5"): it does not support '&&'/'||' or $PSStyle \u2014 join commands with ';' or split into multiple calls. metadata.shellKind="pwsh" means PowerShell 7+ with full syntax. Read resolvedShell/shellKind from any execShell result before writing Windows-specific syntax. LIFECYCLE CONTRACT (misreading this contract silenced a real dialog for 10 hours): (1) A normal command waits synchronously for at most 120 seconds; if it ends inside that window the call IS finished \u2014 a result containing exitCode means the command has already completed, never "still running". (2) Only a result with metadata.detached === true AND a taskId represents a tracked background task that may trigger completion wake-up (terminal completion turn carrying a bounded result capsule); that handle may be waited on with taskWait and will produce a completion notification. (3) Text the command itself prints (LAUNCHED, STARTED, BACKGROUND, ...) is plain stdout with zero lifecycle meaning \u2014 never read it as a platform receipt. (4) Shell-level backgrounding (\`&\`, nohup, disown, setsid, self-daemonizing scripts, PowerShell Start-Process) is NOT tracked: those descendants produce no completion notification, and the outer shell finishing tells you nothing about them. Use background: true to turn a finite job into a tracked background task immediately. When a synchronous result's core output was redirected to a file, read that file in the SAME turn \u2014 never end the turn promising a later automatic report without a taskId.`,
|
|
17392
17650
|
parameters: {
|
|
17393
17651
|
type: "object",
|
|
17394
17652
|
properties: {
|
|
@@ -17399,6 +17657,10 @@ function buildExecShellToolDefinition(toolName = "execShell") {
|
|
|
17399
17657
|
cmd: {
|
|
17400
17658
|
type: "string",
|
|
17401
17659
|
description: "Compatibility alias for command."
|
|
17660
|
+
},
|
|
17661
|
+
background: {
|
|
17662
|
+
type: "boolean",
|
|
17663
|
+
description: "Immediately detach this command into a tracked background task (no 120s synchronous wait): returns {detached: true, taskId}, and on completion the platform resumes this conversation with a bounded result capsule (exitCode, duration, stdout/stderr tail). Default false. Use for finite jobs (tests, builds, batch) whose result you need later; NOT for dev servers/watchers \u2014 use launchProcess for those."
|
|
17402
17664
|
}
|
|
17403
17665
|
}
|
|
17404
17666
|
}
|
|
@@ -17439,11 +17701,13 @@ function normalizeExecShellInput(input) {
|
|
|
17439
17701
|
throw new Error("execShell requires a non-empty command.");
|
|
17440
17702
|
}
|
|
17441
17703
|
const activity = extractActivity(record);
|
|
17704
|
+
const background = record.background === true || typeof record.background === "string" && record.background.trim().toLowerCase() === "true";
|
|
17442
17705
|
return {
|
|
17443
17706
|
command,
|
|
17444
17707
|
...record.shell !== void 0 ? { shell: record.shell } : {},
|
|
17445
17708
|
...activity ? { activity } : {},
|
|
17446
|
-
...record.input !== void 0 ? { input: record.input } : {}
|
|
17709
|
+
...record.input !== void 0 ? { input: record.input } : {},
|
|
17710
|
+
...background ? { background: true } : {}
|
|
17447
17711
|
};
|
|
17448
17712
|
}
|
|
17449
17713
|
throw new Error("execShell requires a non-empty command.");
|
|
@@ -17481,6 +17745,7 @@ var execShellCapability = {
|
|
|
17481
17745
|
command,
|
|
17482
17746
|
shell: normalized.shell
|
|
17483
17747
|
});
|
|
17748
|
+
const background = normalized.background === true;
|
|
17484
17749
|
const result4 = await runWorkspaceCommand({
|
|
17485
17750
|
workspaceRoot,
|
|
17486
17751
|
command: shellPlan.argv,
|
|
@@ -17488,15 +17753,18 @@ var execShellCapability = {
|
|
|
17488
17753
|
outputLimit: ctx.commandOutputLimit,
|
|
17489
17754
|
commandPrefix: ctx.commandPrefix,
|
|
17490
17755
|
abortSignal: ctx.abortSignal,
|
|
17491
|
-
detachMs: isImmediateDetachShellCommand({ command }) ? 0 : ctx.detachMs,
|
|
17756
|
+
detachMs: background ? 0 : isImmediateDetachShellCommand({ command }) ? 0 : ctx.detachMs,
|
|
17492
17757
|
stdin: typeof normalized.input === "string" ? normalized.input : void 0,
|
|
17493
17758
|
// Ownership of the turn that invoked this command: an auto-detached
|
|
17494
17759
|
// command keeps its parent dialog so its terminal can resume that
|
|
17495
17760
|
// conversation (see processOwnership.ts). Absent ctx.owner = unowned.
|
|
17496
17761
|
owner: ctx.owner ?? null
|
|
17497
17762
|
});
|
|
17763
|
+
const warnings = result4.detached || result4.spawnFailed ? [] : detectExecShellSynchronousWarnings(command, {
|
|
17764
|
+
resolvedShell: shellPlan.resolvedShell
|
|
17765
|
+
});
|
|
17498
17766
|
return {
|
|
17499
|
-
content: result4.content,
|
|
17767
|
+
content: warnings.length > 0 ? [result4.content, ...warnings.map((warning) => warning.message)].join("\n\n") : result4.content,
|
|
17500
17768
|
metadata: {
|
|
17501
17769
|
command,
|
|
17502
17770
|
exitCode: result4.exitCode,
|
|
@@ -17511,8 +17779,21 @@ var execShellCapability = {
|
|
|
17511
17779
|
// Phase 0: stable taskId from the pre-registered Execution
|
|
17512
17780
|
// Envelope (additive; existing detached fields untouched).
|
|
17513
17781
|
taskId: result4.taskId,
|
|
17514
|
-
status: "running"
|
|
17515
|
-
|
|
17782
|
+
status: "running",
|
|
17783
|
+
// P0 machine-readable execution state: an explicit positive
|
|
17784
|
+
// signal instead of requiring the model to infer "not detached"
|
|
17785
|
+
// from absence. Mirrors the real lifecycle contract.
|
|
17786
|
+
executionState: "detached",
|
|
17787
|
+
trackedBackgroundTask: true,
|
|
17788
|
+
completionNotification: "terminal-resume"
|
|
17789
|
+
} : {
|
|
17790
|
+
// P0 machine-readable execution state: the command already
|
|
17791
|
+
// finished inside the synchronous window; no background task
|
|
17792
|
+
// exists, so no completion notification will ever arrive.
|
|
17793
|
+
executionState: "synchronous-complete",
|
|
17794
|
+
trackedBackgroundTask: false,
|
|
17795
|
+
completionNotification: "none"
|
|
17796
|
+
},
|
|
17516
17797
|
...normalized.activity ? { activity: normalized.activity } : {}
|
|
17517
17798
|
}
|
|
17518
17799
|
};
|
|
@@ -17728,7 +18009,7 @@ function buildLaunchProcessTool() {
|
|
|
17728
18009
|
type: "function",
|
|
17729
18010
|
function: {
|
|
17730
18011
|
name: "launchProcess",
|
|
17731
|
-
description: "Start a long-running background process (dev server, watcher, REPL) and return immediately with {pid, label, status}. Use listProcesses to inspect or stop it.",
|
|
18012
|
+
description: "Start a long-running ambient background process (dev server, watcher, REPL) and return immediately with {pid, label, taskId, status}. Real boundaries: this registers a tracked background task, but completion does NOT automatically start a new turn \u2014 the process is intentionally notice-only (an exited dev server must not silently burn a model turn). When it reaches a terminal state the bounded result (exitCode, duration, stdout/stderr tail) is recorded and surfaced as a notice injected only on your next real turn in the same conversation. Use listProcesses to inspect, taskWait to block for its terminal state, or taskStop to stop it. For a finite job (tests, builds, batch) whose result must resume the conversation automatically, use execShell with background: true instead.",
|
|
17732
18013
|
parameters: {
|
|
17733
18014
|
type: "object",
|
|
17734
18015
|
properties: {
|
|
@@ -18869,6 +19150,55 @@ async function execShellTool(args2) {
|
|
|
18869
19150
|
}
|
|
18870
19151
|
);
|
|
18871
19152
|
}
|
|
19153
|
+
var LAUNCH_PROCESS_STREAM_RING_CHARS = 65536;
|
|
19154
|
+
var LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS = 4e6;
|
|
19155
|
+
function createLaunchProcessStreamCapture() {
|
|
19156
|
+
let ring = "";
|
|
19157
|
+
let full = null;
|
|
19158
|
+
let capturedChars = 0;
|
|
19159
|
+
let totalChars = 0;
|
|
19160
|
+
let totalNewlines = 0;
|
|
19161
|
+
return {
|
|
19162
|
+
push(chunk) {
|
|
19163
|
+
const str = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
19164
|
+
if (!str) return;
|
|
19165
|
+
totalChars += str.length;
|
|
19166
|
+
for (let i = 0; i < str.length; i += 1) {
|
|
19167
|
+
if (str.charCodeAt(i) === 10) totalNewlines += 1;
|
|
19168
|
+
}
|
|
19169
|
+
const combined = ring + str;
|
|
19170
|
+
if (combined.length <= LAUNCH_PROCESS_STREAM_RING_CHARS) {
|
|
19171
|
+
ring = combined;
|
|
19172
|
+
return;
|
|
19173
|
+
}
|
|
19174
|
+
if (full === null) {
|
|
19175
|
+
full = [combined];
|
|
19176
|
+
capturedChars = combined.length;
|
|
19177
|
+
} else if (capturedChars < LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS) {
|
|
19178
|
+
const room = LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS - capturedChars;
|
|
19179
|
+
const slice = str.length <= room ? str : str.slice(0, room);
|
|
19180
|
+
full.push(slice);
|
|
19181
|
+
capturedChars += slice.length;
|
|
19182
|
+
}
|
|
19183
|
+
ring = combined.slice(-LAUNCH_PROCESS_STREAM_RING_CHARS);
|
|
19184
|
+
},
|
|
19185
|
+
text() {
|
|
19186
|
+
return full === null ? ring : full.join("");
|
|
19187
|
+
},
|
|
19188
|
+
tailText() {
|
|
19189
|
+
return ring;
|
|
19190
|
+
},
|
|
19191
|
+
evicted() {
|
|
19192
|
+
return full !== null;
|
|
19193
|
+
},
|
|
19194
|
+
totalChars() {
|
|
19195
|
+
return totalChars;
|
|
19196
|
+
},
|
|
19197
|
+
totalLines() {
|
|
19198
|
+
return totalChars > 0 ? totalNewlines + 1 : 0;
|
|
19199
|
+
}
|
|
19200
|
+
};
|
|
19201
|
+
}
|
|
18872
19202
|
async function launchProcessTool(args2) {
|
|
18873
19203
|
const parsed = parseWorkspaceToolArguments(args2.call.arguments);
|
|
18874
19204
|
const command = requireShellCommand(parsed, args2.call.name);
|
|
@@ -18896,6 +19226,14 @@ async function launchProcessTool(args2) {
|
|
|
18896
19226
|
stdio: ["ignore", "pipe", "pipe"],
|
|
18897
19227
|
detached
|
|
18898
19228
|
});
|
|
19229
|
+
const stdoutCapture = createLaunchProcessStreamCapture();
|
|
19230
|
+
const stderrCapture = createLaunchProcessStreamCapture();
|
|
19231
|
+
proc.stdout?.on("data", (chunk) => stdoutCapture.push(chunk));
|
|
19232
|
+
proc.stderr?.on("data", (chunk) => stderrCapture.push(chunk));
|
|
19233
|
+
proc.stdout?.on("error", () => {
|
|
19234
|
+
});
|
|
19235
|
+
proc.stderr?.on("error", () => {
|
|
19236
|
+
});
|
|
18899
19237
|
const pid = proc.pid;
|
|
18900
19238
|
if (typeof pid !== "number") {
|
|
18901
19239
|
return {
|
|
@@ -18936,7 +19274,38 @@ async function launchProcessTool(args2) {
|
|
|
18936
19274
|
process.removeListener("SIGTERM", cleanupChildOnHostSignal);
|
|
18937
19275
|
process.removeListener("SIGINT", cleanupChildOnHostSignal);
|
|
18938
19276
|
}
|
|
18939
|
-
|
|
19277
|
+
const exitCode = code ?? 1;
|
|
19278
|
+
let capsule;
|
|
19279
|
+
try {
|
|
19280
|
+
capsule = buildProcessTaskResultCapsule({
|
|
19281
|
+
// The full captured output (bounded prefix when the process was
|
|
19282
|
+
// extremely chatty): the module tails it for the inline capsule and
|
|
19283
|
+
// spills it for recovery. Provenance carries the live original-stream
|
|
19284
|
+
// counters so truncated/spill metadata never derives from the ring.
|
|
19285
|
+
stdout: stdoutCapture.text(),
|
|
19286
|
+
stderr: stderrCapture.text(),
|
|
19287
|
+
stdoutTail: stdoutCapture.tailText(),
|
|
19288
|
+
stderrTail: stderrCapture.tailText(),
|
|
19289
|
+
exitCode,
|
|
19290
|
+
// Duration measured from the registry envelope's startedAt (spawn).
|
|
19291
|
+
startedAt: envelope.startedAt,
|
|
19292
|
+
workspaceRoot: args2.workspaceRoot,
|
|
19293
|
+
streamProvenance: {
|
|
19294
|
+
stdout: {
|
|
19295
|
+
evicted: stdoutCapture.evicted(),
|
|
19296
|
+
totalChars: stdoutCapture.totalChars(),
|
|
19297
|
+
totalLines: stdoutCapture.totalLines()
|
|
19298
|
+
},
|
|
19299
|
+
stderr: {
|
|
19300
|
+
evicted: stderrCapture.evicted(),
|
|
19301
|
+
totalChars: stderrCapture.totalChars(),
|
|
19302
|
+
totalLines: stderrCapture.totalLines()
|
|
19303
|
+
}
|
|
19304
|
+
}
|
|
19305
|
+
});
|
|
19306
|
+
} catch {
|
|
19307
|
+
}
|
|
19308
|
+
registry2.markExited(pid, exitCode, capsule);
|
|
18940
19309
|
});
|
|
18941
19310
|
const activity = extractActivity(parsed);
|
|
18942
19311
|
const resultData = { pid, label, taskId: envelope.taskId, status: "running" };
|
|
@@ -18948,6 +19317,13 @@ async function launchProcessTool(args2) {
|
|
|
18948
19317
|
label,
|
|
18949
19318
|
status: "running",
|
|
18950
19319
|
taskId: envelope.taskId,
|
|
19320
|
+
// P0 machine-readable execution state (consistent with the real wake
|
|
19321
|
+
// contract): a real background task from birth, but ambient — its
|
|
19322
|
+
// terminal result rides the next real turn's notice, not an automatic
|
|
19323
|
+
// completion turn.
|
|
19324
|
+
executionState: "background",
|
|
19325
|
+
trackedBackgroundTask: true,
|
|
19326
|
+
completionNotification: "next-turn-notice",
|
|
18951
19327
|
processLaunch: resultData,
|
|
18952
19328
|
...activity ? { activity } : {}
|
|
18953
19329
|
}
|