nolo-cli 0.34.0-alpha.1 → 0.35.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-BUYCXZCQ.js → _executeModel-RV52PPE3.js} +11 -11
- package/{agentDeleteCommand-WSS2IJVM.js → agentDeleteCommand-FBBW3IIM.js} +2 -2
- package/{agentEmailCommands-SHTPJTSX.js → agentEmailCommands-7F4PCABZ.js} +12 -12
- package/{agentListCommands-GV6UYVAF.js → agentListCommands-ZQXPSB6U.js} +2 -2
- package/{agentMachineCommands-FLD7TAFH.js → agentMachineCommands-E3NB67GM.js} +3 -3
- package/{agentRecordCommands-57W6R7QW.js → agentRecordCommands-7PXWHCQM.js} +12 -12
- package/{agentRunCommand-HJGXSMDN.js → agentRunCommand-Z4654ONK.js} +13 -13
- package/{agentRunControl-GXOB47YI.js → agentRunControl-7MUPSYDQ.js} +3 -1
- package/{agentRuntimeLocal-G7ZV55I6.js → agentRuntimeLocal-OBKWKEKW.js} +2 -2
- package/{agentSlice-HFR7SP3B.js → agentSlice-B2IMSPH5.js} +1 -1
- package/{agentSuperviseCommands-Q64S5SPG.js → agentSuperviseCommands-MKWW3ICX.js} +3 -3
- package/{appDeleteCommands-SHHE56Y2.js → appDeleteCommands-72BE7DSA.js} +2 -2
- package/{appDeployCommands-WUMSO2VS.js → appDeployCommands-XJBLKCYO.js} +2 -2
- package/{appDeployStatusCommands-VZSVN6SR.js → appDeployStatusCommands-EC4HWCBQ.js} +2 -2
- package/{appGetCommands-ZJ2C3A5Y.js → appGetCommands-62BAATPL.js} +2 -2
- package/{appListCommands-W33L6U5X.js → appListCommands-5EZCONM7.js} +2 -2
- package/{chatQueueReduxAdapter-SYIX4ODY.js → chatQueueReduxAdapter-SSF3DNBX.js} +2 -2
- package/{chunk-AUH2LTIY.js → chunk-55C3UZSC.js} +3 -3
- package/{chunk-6KYCG5GW.js → chunk-5Q4DXT47.js} +9 -9
- package/{chunk-DX6BP4K7.js → chunk-7KY6U7MW.js} +8 -8
- package/{chunk-VSA6QAGV.js → chunk-E2OQ5N2F.js} +40 -10
- package/{chunk-LQ6PUHW6.js → chunk-EFN2AFWF.js} +252 -64
- package/{chunk-4HOXB6SY.js → chunk-IMHO5LC6.js} +100 -27
- package/{chunk-VD2SO4RG.js → chunk-INXNBCEY.js} +2 -2
- package/{chunk-HDT2RB7T.js → chunk-KPJYUEPE.js} +1 -1
- package/{chunk-3LWZNEGQ.js → chunk-LFOILL6R.js} +1 -1
- package/{chunk-LAUUAYLK.js → chunk-OCJNJUR4.js} +2 -2
- package/{chunk-VBD7W4Q6.js → chunk-OGPQ3AMC.js} +14 -10
- package/{chunk-FDKEDU7R.js → chunk-P62APAXL.js} +2 -2
- package/{chunk-MI2J67UU.js → chunk-P66M7LSQ.js} +16 -1
- package/{chunk-KCBK4MDT.js → chunk-RUVWTAK7.js} +3 -3
- package/{chunk-3XZUNRYF.js → chunk-SDAPQUSO.js} +7 -7
- package/{chunk-74IQ5RRO.js → chunk-Z7D7MBWR.js} +1 -1
- package/{chunk-YRP7LG4R.js → chunk-ZOEE4OVX.js} +7 -16
- package/{chunk-7YDKVLAE.js → chunk-ZZDWMZFU.js} +3 -3
- package/{createAgentAutomationAction-BH3D2L2H.js → createAgentAutomationAction-IDIY6K45.js} +3 -3
- package/{createDialogAction-U4LDV2RW.js → createDialogAction-5LB4YBPS.js} +4 -4
- package/{createPageAction-Q2ANG535.js → createPageAction-TJWVDPZD.js} +3 -3
- package/{dialogCommands-C556D6WD.js → dialogCommands-44NGBPJJ.js} +2 -2
- package/{dialogSlice-P2VSKG2X.js → dialogSlice-62JC4ZMX.js} +2 -2
- package/{docCreateCommands-AAVGLQWE.js → docCreateCommands-IOGYJ7F7.js} +2 -2
- package/{docDeleteCommands-PKMKSQD2.js → docDeleteCommands-RO4BZWFP.js} +2 -2
- package/{docReadCommands-2WUH7UXC.js → docReadCommands-F2KNOJFW.js} +2 -2
- package/{docStore-J5UWRBB4.js → docStore-ZRNKZZXU.js} +4 -4
- package/{docUpdateCommands-B2N4E5HQ.js → docUpdateCommands-2VG4AYOC.js} +2 -2
- package/{handleSendMessageAction-RRZ53YYH.js → handleSendMessageAction-YB64A32O.js} +2 -2
- package/index.js +46 -46
- package/{machineCommands-CWKMJG33.js → machineCommands-6WPICTON.js} +12 -12
- package/{machineWsRunDispatch-UYNVNX4A.js → machineWsRunDispatch-4OGEZTYG.js} +12 -12
- package/package.json +1 -1
- package/{readlineWorkspace-VYELP22P.js → readlineWorkspace-BZAZ5YXM.js} +127 -78
- package/{runAgentClientLoop-35GKZDOZ.js → runAgentClientLoop-SOZS3YTQ.js} +8 -8
- package/{spaceCommands-AAKQM2PM.js → spaceCommands-WEASK3IV.js} +2 -2
- package/{spaceSlice-NSNGR4Y2.js → spaceSlice-2QNUKRN6.js} +3 -3
- package/{streamAgentChatTurn-D5IDHXMV.js → streamAgentChatTurn-BYI7UXAA.js} +10 -10
- package/{tools-JQOETZP5.js → tools-E6FEUNZY.js} +6 -6
- package/{updateDialogTitleAction-WOTSDSFQ.js → updateDialogTitleAction-2A3BEXSP.js} +3 -3
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./chunk-AGGIDPCZ.js";
|
|
14
14
|
import {
|
|
15
15
|
runLlm
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-55C3UZSC.js";
|
|
17
17
|
import {
|
|
18
18
|
cleanupCliSessionForDialog
|
|
19
19
|
} from "./chunk-MGIHZ5CJ.js";
|
|
@@ -3519,7 +3519,7 @@ var messageSlice = createSliceWithThunks({
|
|
|
3519
3519
|
runtimeOptions: args.runtimeOptions
|
|
3520
3520
|
});
|
|
3521
3521
|
if (agentKeyToUse) {
|
|
3522
|
-
const { streamAgentChatTurn } = await import("./agentSlice-
|
|
3522
|
+
const { streamAgentChatTurn } = await import("./agentSlice-B2IMSPH5.js");
|
|
3523
3523
|
await dispatch(
|
|
3524
3524
|
streamAgentChatTurn({
|
|
3525
3525
|
agentKey: agentKeyToUse,
|
|
@@ -4742,7 +4742,7 @@ var deleteDialogThunk = async (payload, thunkApi) => {
|
|
|
4742
4742
|
}
|
|
4743
4743
|
if (isCurrentDialog) {
|
|
4744
4744
|
await thunkApi.dispatch(resetMsgs());
|
|
4745
|
-
const { clearPendingAttachments: clearPendingAttachments2 } = await import("./dialogSlice-
|
|
4745
|
+
const { clearPendingAttachments: clearPendingAttachments2 } = await import("./dialogSlice-62JC4ZMX.js");
|
|
4746
4746
|
dispatch(clearPendingAttachments2());
|
|
4747
4747
|
}
|
|
4748
4748
|
return { dialogKey, isCurrentDialog, attachmentPlan };
|
|
@@ -4750,15 +4750,15 @@ var deleteDialogThunk = async (payload, thunkApi) => {
|
|
|
4750
4750
|
|
|
4751
4751
|
// packages/chat/dialog/dialogSlice.ts
|
|
4752
4752
|
var runCreateDialogAction = async (args, thunkApi) => {
|
|
4753
|
-
const { createDialogAction } = await import("./createDialogAction-
|
|
4753
|
+
const { createDialogAction } = await import("./createDialogAction-5LB4YBPS.js");
|
|
4754
4754
|
return createDialogAction(args, thunkApi);
|
|
4755
4755
|
};
|
|
4756
4756
|
var runCreateAgentAutomationAction = async (args, thunkApi) => {
|
|
4757
|
-
const { createAgentAutomationAction } = await import("./createAgentAutomationAction-
|
|
4757
|
+
const { createAgentAutomationAction } = await import("./createAgentAutomationAction-IDIY6K45.js");
|
|
4758
4758
|
return createAgentAutomationAction(args, thunkApi);
|
|
4759
4759
|
};
|
|
4760
4760
|
var runUpdateDialogTitleAction = async (args, thunkApi) => {
|
|
4761
|
-
const { updateDialogTitleAction } = await import("./updateDialogTitleAction-
|
|
4761
|
+
const { updateDialogTitleAction } = await import("./updateDialogTitleAction-2A3BEXSP.js");
|
|
4762
4762
|
return updateDialogTitleAction(args, thunkApi);
|
|
4763
4763
|
};
|
|
4764
4764
|
var runAddDialogAgentAction = async (args, thunkApi) => {
|
|
@@ -4782,7 +4782,7 @@ var runSetDialogExtraReferencesAction = async (args, thunkApi) => {
|
|
|
4782
4782
|
return setDialogExtraReferencesAction(args, thunkApi);
|
|
4783
4783
|
};
|
|
4784
4784
|
var runHandleSendMessageAction = async (args, thunkApi) => {
|
|
4785
|
-
const { handleSendMessageAction } = await import("./handleSendMessageAction-
|
|
4785
|
+
const { handleSendMessageAction } = await import("./handleSendMessageAction-YB64A32O.js");
|
|
4786
4786
|
return handleSendMessageAction(args, thunkApi);
|
|
4787
4787
|
};
|
|
4788
4788
|
function clearDialogState() {
|
|
@@ -4795,7 +4795,7 @@ var createPageAndAddReference = createAsyncThunk(
|
|
|
4795
4795
|
async (payload, { dispatch, getState, rejectWithValue }) => {
|
|
4796
4796
|
const { slateData, jsonData, title, type, fileId, groupId, dialogKey } = payload;
|
|
4797
4797
|
try {
|
|
4798
|
-
const { createDocState } = await import("./docStore-
|
|
4798
|
+
const { createDocState } = await import("./docStore-ZRNKZZXU.js");
|
|
4799
4799
|
const pageKey = await createDocState(
|
|
4800
4800
|
{ slateData, title },
|
|
4801
4801
|
{ dispatch, getState }
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
|
|
9
9
|
// packages/cli/agentRunControl.ts
|
|
10
10
|
import { homedir as nodeHomedir } from "node:os";
|
|
11
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
11
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
12
12
|
import * as nodeFs from "node:fs";
|
|
13
13
|
import { execFileSync as nodeExecFileSync, spawn as nodeSpawn } from "node:child_process";
|
|
14
14
|
var RUN_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -380,16 +380,42 @@ function rewriteMsgFileArg(args, messagePath) {
|
|
|
380
380
|
}
|
|
381
381
|
return result;
|
|
382
382
|
}
|
|
383
|
-
function
|
|
383
|
+
function resolveValidCliEntrypoint(cliEntrypointPath, fs = nodeFs) {
|
|
384
|
+
if (cliEntrypointPath && fs.existsSync(cliEntrypointPath)) {
|
|
385
|
+
return cliEntrypointPath;
|
|
386
|
+
}
|
|
387
|
+
const fallback = resolveCliEntrypointPath();
|
|
388
|
+
if (fs.existsSync(fallback)) {
|
|
389
|
+
return fallback;
|
|
390
|
+
}
|
|
391
|
+
const candidates = [
|
|
392
|
+
join(dirname(fallback), "cli", "index.ts"),
|
|
393
|
+
join(dirname(fallback), "cli", "index.js"),
|
|
394
|
+
join(dirname(fallback), "..", "packages", "cli", "index.ts"),
|
|
395
|
+
join(dirname(fallback), "..", "packages", "cli", "index.js"),
|
|
396
|
+
join(dirname(fallback), "..", "index.js")
|
|
397
|
+
];
|
|
398
|
+
for (const cand of candidates) {
|
|
399
|
+
if (fs.existsSync(cand)) {
|
|
400
|
+
return cand;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return fallback;
|
|
404
|
+
}
|
|
405
|
+
function buildAgentRunChildCommand(options, deps = {}) {
|
|
406
|
+
const fs = deps.fs ?? nodeFs;
|
|
384
407
|
const execPath = process.execPath;
|
|
385
|
-
const entrypoint = options.cliEntrypointPath || resolveCliEntrypointPath();
|
|
386
408
|
const commandParts = options.commandPath ?? [];
|
|
387
409
|
let strippedArgs = stripBackgroundFlag(options.rawArgs);
|
|
388
410
|
if (options.messagePath) {
|
|
389
411
|
strippedArgs = rewriteMsgFileArg(strippedArgs, options.messagePath);
|
|
390
412
|
}
|
|
391
413
|
strippedArgs = absolutizeSkillArgs(strippedArgs);
|
|
392
|
-
if (isCompiledBinary() ||
|
|
414
|
+
if (isCompiledBinary() || options.cliEntrypointPath === execPath) {
|
|
415
|
+
return { execPath, childArgs: [...commandParts, ...strippedArgs] };
|
|
416
|
+
}
|
|
417
|
+
const entrypoint = resolveValidCliEntrypoint(options.cliEntrypointPath, fs);
|
|
418
|
+
if (entrypoint === execPath) {
|
|
393
419
|
return { execPath, childArgs: [...commandParts, ...strippedArgs] };
|
|
394
420
|
}
|
|
395
421
|
return { execPath, childArgs: [entrypoint, ...commandParts, ...strippedArgs] };
|
|
@@ -453,12 +479,15 @@ async function spawnLocalBackgroundRun(input, deps = {}) {
|
|
|
453
479
|
...spawnHead ? { spawnHead } : {}
|
|
454
480
|
};
|
|
455
481
|
fs.writeFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
456
|
-
const { execPath, childArgs } = buildAgentRunChildCommand(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
482
|
+
const { execPath, childArgs } = buildAgentRunChildCommand(
|
|
483
|
+
{
|
|
484
|
+
rawArgs: input.rawArgs,
|
|
485
|
+
commandPath: input.commandPath,
|
|
486
|
+
cliEntrypointPath: input.cliEntrypointPath,
|
|
487
|
+
messagePath
|
|
488
|
+
},
|
|
489
|
+
deps
|
|
490
|
+
);
|
|
462
491
|
const childEnv = {
|
|
463
492
|
...env,
|
|
464
493
|
NOLO_AGENT_RUN_CHILD: "1",
|
|
@@ -1042,6 +1071,7 @@ export {
|
|
|
1042
1071
|
stripBackgroundFlag,
|
|
1043
1072
|
absolutizeSkillArgs,
|
|
1044
1073
|
rewriteMsgFileArg,
|
|
1074
|
+
buildAgentRunChildCommand,
|
|
1045
1075
|
spawnLocalBackgroundRun,
|
|
1046
1076
|
ackRunRecord,
|
|
1047
1077
|
DEFAULT_ACK_LEASE_TTL_MS,
|
|
@@ -23,10 +23,11 @@ import {
|
|
|
23
23
|
} from "./chunk-T27IEBNQ.js";
|
|
24
24
|
import {
|
|
25
25
|
EMPTY_ASSISTANT_REPAIR_PROMPT,
|
|
26
|
+
formatLengthTruncatedReasoningTail,
|
|
26
27
|
hasAssistantVisibleOutput,
|
|
27
28
|
resolveEmptyAssistantFallbackMessage,
|
|
28
29
|
resolveEmptyAssistantOutcome
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-P66M7LSQ.js";
|
|
30
31
|
import {
|
|
31
32
|
createThinkParserState,
|
|
32
33
|
createToolCallAccumulator,
|
|
@@ -721,6 +722,191 @@ async function maybeAutoCompactLocalHistory(args) {
|
|
|
721
722
|
}
|
|
722
723
|
}
|
|
723
724
|
|
|
725
|
+
// packages/agent-runtime/progressGuard.ts
|
|
726
|
+
var DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS = 5;
|
|
727
|
+
var DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS = 8;
|
|
728
|
+
function sortJsonKeys(obj) {
|
|
729
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
730
|
+
if (Array.isArray(obj)) return obj.map(sortJsonKeys);
|
|
731
|
+
const sorted = {};
|
|
732
|
+
for (const key of Object.keys(obj).sort()) {
|
|
733
|
+
sorted[key] = sortJsonKeys(obj[key]);
|
|
734
|
+
}
|
|
735
|
+
return sorted;
|
|
736
|
+
}
|
|
737
|
+
function normalizeToolArguments(rawArgs) {
|
|
738
|
+
if (typeof rawArgs !== "string") {
|
|
739
|
+
try {
|
|
740
|
+
return JSON.stringify(sortJsonKeys(rawArgs ?? ""));
|
|
741
|
+
} catch {
|
|
742
|
+
return String(rawArgs ?? "");
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
const trimmed = rawArgs.trim();
|
|
746
|
+
if (!trimmed) return "";
|
|
747
|
+
try {
|
|
748
|
+
const parsed = JSON.parse(trimmed);
|
|
749
|
+
return JSON.stringify(sortJsonKeys(parsed));
|
|
750
|
+
} catch {
|
|
751
|
+
return trimmed;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
function extractNormalizedContentText(content) {
|
|
755
|
+
if (typeof content === "string") return content.trim();
|
|
756
|
+
if (!Array.isArray(content)) return "";
|
|
757
|
+
return content.map((part) => {
|
|
758
|
+
if (part?.type === "text") return String(part.text ?? "").trim();
|
|
759
|
+
if (part?.type === "image_url") return `[img:${part.image_url?.url ?? ""}]`;
|
|
760
|
+
return "";
|
|
761
|
+
}).filter(Boolean).join("\n");
|
|
762
|
+
}
|
|
763
|
+
function buildToolCallsSignature(toolCalls) {
|
|
764
|
+
if (!toolCalls || toolCalls.length === 0) return "";
|
|
765
|
+
return toolCalls.map(
|
|
766
|
+
(tc) => `${tc.function?.name ?? ""}:${normalizeToolArguments(tc.function?.arguments)}`
|
|
767
|
+
).join(";;");
|
|
768
|
+
}
|
|
769
|
+
function buildAssistantActionFingerprint(result) {
|
|
770
|
+
const content = extractNormalizedContentText(result.content);
|
|
771
|
+
const reasoning = typeof result.reasoning_content === "string" ? result.reasoning_content.trim() : "";
|
|
772
|
+
let toolCallsSig = buildToolCallsSignature(result.tool_calls);
|
|
773
|
+
let hasToolCalls = Array.isArray(result.tool_calls) && result.tool_calls.length > 0;
|
|
774
|
+
if (!toolCallsSig && Array.isArray(result.output) && result.output.length > 0) {
|
|
775
|
+
const blocksToolCalls = result.output.filter(
|
|
776
|
+
(b) => b.type === "toolCall"
|
|
777
|
+
).map((b) => b.toolCall);
|
|
778
|
+
if (blocksToolCalls.length > 0) {
|
|
779
|
+
toolCallsSig = buildToolCallsSignature(blocksToolCalls);
|
|
780
|
+
hasToolCalls = true;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
const hasNonEmptyText = content.length > 0 || reasoning.length > 0;
|
|
784
|
+
const fingerprint = `${content}##${reasoning}##${toolCallsSig}`;
|
|
785
|
+
return {
|
|
786
|
+
fingerprint,
|
|
787
|
+
hasNonEmptyText,
|
|
788
|
+
hasToolCalls,
|
|
789
|
+
toolCallsSignature: toolCallsSig
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function buildToolResultsSignature(toolExecutions) {
|
|
793
|
+
if (toolExecutions.length === 0) return "";
|
|
794
|
+
return toolExecutions.map((exec) => {
|
|
795
|
+
const contentStr = typeof exec.content === "string" ? exec.content.trim() : "";
|
|
796
|
+
const metaStr = exec.metadata ? JSON.stringify(sortJsonKeys(exec.metadata)) : "";
|
|
797
|
+
return `${exec.toolName}:${contentStr}:${metaStr}`;
|
|
798
|
+
}).join(";;");
|
|
799
|
+
}
|
|
800
|
+
function resolveProgressGuardConfig(options, env = typeof process !== "undefined" ? process.env : {}) {
|
|
801
|
+
const envDisabled = env.NOLO_LOOP_PROGRESS_GUARD_DISABLED === "true" || env.NOLO_LOOP_PROGRESS_GUARD_DISABLED === "1";
|
|
802
|
+
const disabled = options?.disabled ?? envDisabled ?? false;
|
|
803
|
+
const parsePositiveInt = (val, fallback) => {
|
|
804
|
+
if (typeof val === "number" && Number.isFinite(val) && val >= 1) {
|
|
805
|
+
return Math.floor(val);
|
|
806
|
+
}
|
|
807
|
+
if (typeof val === "string") {
|
|
808
|
+
const parsed = parseInt(val, 10);
|
|
809
|
+
if (Number.isFinite(parsed) && parsed >= 1) {
|
|
810
|
+
return parsed;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return fallback;
|
|
814
|
+
};
|
|
815
|
+
const maxConsecutiveIdenticalRounds = parsePositiveInt(
|
|
816
|
+
options?.maxConsecutiveIdenticalRounds ?? env.NOLO_LOOP_MAX_IDENTICAL_ROUNDS,
|
|
817
|
+
DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS
|
|
818
|
+
);
|
|
819
|
+
const maxConsecutiveStagnantToolRounds = parsePositiveInt(
|
|
820
|
+
options?.maxConsecutiveStagnantToolRounds ?? env.NOLO_LOOP_MAX_STAGNANT_TOOL_ROUNDS,
|
|
821
|
+
DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS
|
|
822
|
+
);
|
|
823
|
+
return {
|
|
824
|
+
disabled,
|
|
825
|
+
maxConsecutiveIdenticalRounds,
|
|
826
|
+
maxConsecutiveStagnantToolRounds
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
var LocalLoopProgressGuard = class {
|
|
830
|
+
config;
|
|
831
|
+
lastAssistantFingerprint = null;
|
|
832
|
+
lastAssistantHasNonEmptyText = false;
|
|
833
|
+
consecutiveIdenticalCount = 0;
|
|
834
|
+
lastToolCallsSignature = null;
|
|
835
|
+
lastToolResultsSignature = null;
|
|
836
|
+
consecutiveStagnantToolCount = 0;
|
|
837
|
+
constructor(config, env) {
|
|
838
|
+
this.config = resolveProgressGuardConfig(config, env);
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* 在 LLM 返回 assistant 响应后调用。
|
|
842
|
+
* 仅在纯文本/无工具调用且实质文本相同达到阈值时直接熔断;
|
|
843
|
+
* 若存在工具调用,则不在此处熔断,留待工具执行后结合工具结果综合判定。
|
|
844
|
+
*/
|
|
845
|
+
observeAssistantResponse(result) {
|
|
846
|
+
if (this.config.disabled) return { action: "continue" };
|
|
847
|
+
const { fingerprint, hasNonEmptyText, hasToolCalls } = buildAssistantActionFingerprint(result);
|
|
848
|
+
if (this.lastAssistantFingerprint !== null && this.lastAssistantFingerprint === fingerprint) {
|
|
849
|
+
this.consecutiveIdenticalCount += 1;
|
|
850
|
+
if (!hasToolCalls && hasNonEmptyText && this.consecutiveIdenticalCount >= this.config.maxConsecutiveIdenticalRounds) {
|
|
851
|
+
return {
|
|
852
|
+
action: "stall",
|
|
853
|
+
reason: "repetition_loop",
|
|
854
|
+
detail: `Consecutive identical text-only assistant response for ${this.consecutiveIdenticalCount} rounds.`,
|
|
855
|
+
consecutiveRounds: this.consecutiveIdenticalCount
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
} else {
|
|
859
|
+
this.lastAssistantFingerprint = fingerprint;
|
|
860
|
+
this.lastAssistantHasNonEmptyText = hasNonEmptyText;
|
|
861
|
+
this.consecutiveIdenticalCount = 1;
|
|
862
|
+
}
|
|
863
|
+
return { action: "continue" };
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* 在本轮所有工具执行完毕后调用,检查是否陷入停滞死循环。
|
|
867
|
+
* 只有当工具返回内容与元数据完全无变化时才计入无进展 streak;
|
|
868
|
+
* 一旦工具结果发生变化(如合法轮询状态推进),立即重置 identical streak,确保绝不误杀正常长任务。
|
|
869
|
+
*/
|
|
870
|
+
observeToolExecution(toolCalls, toolResults) {
|
|
871
|
+
if (this.config.disabled) return { action: "continue" };
|
|
872
|
+
if (!toolCalls || toolCalls.length === 0) return { action: "continue" };
|
|
873
|
+
const toolCallsSig = buildToolCallsSignature(toolCalls);
|
|
874
|
+
const toolResultsSig = buildToolResultsSignature(toolResults);
|
|
875
|
+
const toolCallsUnchanged = this.lastToolCallsSignature !== null && this.lastToolCallsSignature === toolCallsSig;
|
|
876
|
+
const toolResultsUnchanged = this.lastToolResultsSignature !== null && this.lastToolResultsSignature === toolResultsSig;
|
|
877
|
+
if (toolCallsUnchanged && toolResultsUnchanged) {
|
|
878
|
+
this.consecutiveStagnantToolCount += 1;
|
|
879
|
+
if (this.consecutiveIdenticalCount >= this.config.maxConsecutiveIdenticalRounds) {
|
|
880
|
+
return {
|
|
881
|
+
action: "stall",
|
|
882
|
+
reason: "repetition_loop",
|
|
883
|
+
detail: `Consecutive identical assistant response with unchanged tool results for ${this.consecutiveIdenticalCount} rounds.`,
|
|
884
|
+
consecutiveRounds: this.consecutiveIdenticalCount
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
if (this.consecutiveStagnantToolCount >= this.config.maxConsecutiveStagnantToolRounds) {
|
|
888
|
+
return {
|
|
889
|
+
action: "stall",
|
|
890
|
+
reason: "stagnant_tool_calls",
|
|
891
|
+
detail: `Consecutive stagnant tool calls with unchanged results for ${this.consecutiveStagnantToolCount} rounds.`,
|
|
892
|
+
consecutiveRounds: this.consecutiveStagnantToolCount
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
} else {
|
|
896
|
+
this.lastToolCallsSignature = toolCallsSig;
|
|
897
|
+
this.lastToolResultsSignature = toolResultsSig;
|
|
898
|
+
this.consecutiveStagnantToolCount = 1;
|
|
899
|
+
if (!toolResultsUnchanged) {
|
|
900
|
+
this.consecutiveIdenticalCount = 1;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
return { action: "continue" };
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
function createLocalLoopProgressGuard(config, env) {
|
|
907
|
+
return new LocalLoopProgressGuard(config, env);
|
|
908
|
+
}
|
|
909
|
+
|
|
724
910
|
// packages/agent-runtime/localLoop.ts
|
|
725
911
|
var LOCAL_AGENT_CONFIG_MISSING_CODE = "LOCAL_AGENT_CONFIG_MISSING";
|
|
726
912
|
function formatToolExecutionError(args) {
|
|
@@ -1489,6 +1675,7 @@ async function runLocalAgentTurn(input) {
|
|
|
1489
1675
|
let reasoningEmptyRepairCount = 0;
|
|
1490
1676
|
let skipFinalAppend = false;
|
|
1491
1677
|
let partialContent = "";
|
|
1678
|
+
const progressGuard = createLocalLoopProgressGuard(input.progressGuardConfig);
|
|
1492
1679
|
try {
|
|
1493
1680
|
const provider = await resolveProviderOnce();
|
|
1494
1681
|
while (true) {
|
|
@@ -1548,6 +1735,23 @@ async function runLocalAgentTurn(input) {
|
|
|
1548
1735
|
...result.provider || agentConfig.provider ? { provider: result.provider || agentConfig.provider } : {}
|
|
1549
1736
|
});
|
|
1550
1737
|
}
|
|
1738
|
+
const assistantGuardVerdict = progressGuard.observeAssistantResponse(result);
|
|
1739
|
+
if (assistantGuardVerdict.action === "stall") {
|
|
1740
|
+
emitLoopEvent(input, {
|
|
1741
|
+
kind: "loop-stalled",
|
|
1742
|
+
round,
|
|
1743
|
+
reason: assistantGuardVerdict.reason,
|
|
1744
|
+
detail: assistantGuardVerdict.detail,
|
|
1745
|
+
consecutiveRounds: assistantGuardVerdict.consecutiveRounds,
|
|
1746
|
+
atMs: Date.now()
|
|
1747
|
+
});
|
|
1748
|
+
result = {
|
|
1749
|
+
...result,
|
|
1750
|
+
content: resolveEmptyAssistantFallbackMessage(assistantGuardVerdict.reason),
|
|
1751
|
+
emptyAssistantFallbackReason: assistantGuardVerdict.reason
|
|
1752
|
+
};
|
|
1753
|
+
break;
|
|
1754
|
+
}
|
|
1551
1755
|
const toolCalls = result.tool_calls ?? [];
|
|
1552
1756
|
const rawToolCallsCount = (result.tool_calls?.length ?? 0) || (Array.isArray(result.raw_tool_calls) ? result.raw_tool_calls.length : 0);
|
|
1553
1757
|
if (toolCalls.length === 0 && rawToolCallsCount === 0) {
|
|
@@ -1567,9 +1771,21 @@ async function runLocalAgentTurn(input) {
|
|
|
1567
1771
|
continue;
|
|
1568
1772
|
}
|
|
1569
1773
|
if (outcome.kind === "fallback") {
|
|
1774
|
+
if (outcome.reason === "length_truncated" && result.reasoning_content) {
|
|
1775
|
+
const reasoningTailLog = formatLengthTruncatedReasoningTail(result.reasoning_content);
|
|
1776
|
+
if (reasoningTailLog) {
|
|
1777
|
+
console.warn(`
|
|
1778
|
+
${reasoningTailLog}
|
|
1779
|
+
`);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1570
1782
|
result = {
|
|
1571
1783
|
...result,
|
|
1572
|
-
content: resolveEmptyAssistantFallbackMessage(outcome.reason)
|
|
1784
|
+
content: resolveEmptyAssistantFallbackMessage(outcome.reason),
|
|
1785
|
+
// 只打标记不抛错:交互侧仍照常显示诊断文案(零行为变化)。
|
|
1786
|
+
// 上层(后台 run 编排者)据 emptyAssistantFallbackReason 判断
|
|
1787
|
+
// 是否把本轮结算为 failed。见 LocalAgentTurnResult 注释。
|
|
1788
|
+
emptyAssistantFallbackReason: outcome.reason
|
|
1573
1789
|
};
|
|
1574
1790
|
break;
|
|
1575
1791
|
}
|
|
@@ -1605,6 +1821,7 @@ async function runLocalAgentTurn(input) {
|
|
|
1605
1821
|
...result.reasoning_content ? { reasoning_content: result.reasoning_content } : {},
|
|
1606
1822
|
tool_calls: toolCalls
|
|
1607
1823
|
});
|
|
1824
|
+
const executedToolResults = [];
|
|
1608
1825
|
for (const toolCall of toolCalls) {
|
|
1609
1826
|
throwIfAborted(input);
|
|
1610
1827
|
const toolName = toolCall.function.name;
|
|
@@ -1717,6 +1934,11 @@ async function runLocalAgentTurn(input) {
|
|
|
1717
1934
|
}
|
|
1718
1935
|
};
|
|
1719
1936
|
}
|
|
1937
|
+
executedToolResults.push({
|
|
1938
|
+
toolName,
|
|
1939
|
+
content: toolResult.content,
|
|
1940
|
+
metadata: toolResult.metadata
|
|
1941
|
+
});
|
|
1720
1942
|
messages.push({
|
|
1721
1943
|
role: "tool",
|
|
1722
1944
|
content: formatToolMessageContent({
|
|
@@ -1729,6 +1951,26 @@ async function runLocalAgentTurn(input) {
|
|
|
1729
1951
|
...toolResult.metadata ? { tool_result_metadata: toolResult.metadata } : {}
|
|
1730
1952
|
});
|
|
1731
1953
|
}
|
|
1954
|
+
const toolExecutionGuardVerdict = progressGuard.observeToolExecution(
|
|
1955
|
+
toolCalls,
|
|
1956
|
+
executedToolResults
|
|
1957
|
+
);
|
|
1958
|
+
if (toolExecutionGuardVerdict.action === "stall") {
|
|
1959
|
+
emitLoopEvent(input, {
|
|
1960
|
+
kind: "loop-stalled",
|
|
1961
|
+
round,
|
|
1962
|
+
reason: toolExecutionGuardVerdict.reason,
|
|
1963
|
+
detail: toolExecutionGuardVerdict.detail,
|
|
1964
|
+
consecutiveRounds: toolExecutionGuardVerdict.consecutiveRounds,
|
|
1965
|
+
atMs: Date.now()
|
|
1966
|
+
});
|
|
1967
|
+
result = {
|
|
1968
|
+
...result,
|
|
1969
|
+
content: resolveEmptyAssistantFallbackMessage(toolExecutionGuardVerdict.reason),
|
|
1970
|
+
emptyAssistantFallbackReason: toolExecutionGuardVerdict.reason
|
|
1971
|
+
};
|
|
1972
|
+
break;
|
|
1973
|
+
}
|
|
1732
1974
|
round += 1;
|
|
1733
1975
|
}
|
|
1734
1976
|
} catch (error) {
|
|
@@ -3044,14 +3286,6 @@ var NEVER_LOCAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
3044
3286
|
"updateSelf",
|
|
3045
3287
|
"createAgent"
|
|
3046
3288
|
]);
|
|
3047
|
-
var REMOVED_LOCAL_TOOLS = /* @__PURE__ */ new Set([
|
|
3048
|
-
"gitStatus",
|
|
3049
|
-
"gitDiff",
|
|
3050
|
-
"gitCreateBranch",
|
|
3051
|
-
"gitAdd",
|
|
3052
|
-
"gitCommit",
|
|
3053
|
-
"commitWorkspace"
|
|
3054
|
-
]);
|
|
3055
3289
|
var DEFAULT_LOCAL_TOOLS = /* @__PURE__ */ new Set([
|
|
3056
3290
|
"readFile",
|
|
3057
3291
|
"writeFile",
|
|
@@ -3099,13 +3333,6 @@ function resolveLocalToolPolicy(args) {
|
|
|
3099
3333
|
reason: `${toolName} is blocked by the local runtime safety policy.`
|
|
3100
3334
|
};
|
|
3101
3335
|
}
|
|
3102
|
-
if (REMOVED_LOCAL_TOOLS.has(toolName)) {
|
|
3103
|
-
return {
|
|
3104
|
-
allowed: false,
|
|
3105
|
-
toolName,
|
|
3106
|
-
reason: `${toolName} has been removed from the local runtime tool surface. Use execShell in shell-enabled workspace runs when shell is needed.`
|
|
3107
|
-
};
|
|
3108
|
-
}
|
|
3109
3336
|
const allowedByEnv = new Set(parseToolAllowlist(args.env.NOLO_LOCAL_ALLOWED_TOOLS));
|
|
3110
3337
|
const agentTools = new Set(
|
|
3111
3338
|
(args.agentToolNames ?? []).flatMap((item) => {
|
|
@@ -4076,14 +4303,6 @@ var WORKSPACE_TOOL_NAMES = [
|
|
|
4076
4303
|
];
|
|
4077
4304
|
var SHELL_TOOL_NAMES = ["execShell", "launchProcess", "listProcesses"];
|
|
4078
4305
|
var WORKSPACE_TOOL_NAME_SET = new Set(WORKSPACE_TOOL_NAMES);
|
|
4079
|
-
var REMOVED_WORKSPACE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
4080
|
-
"gitStatus",
|
|
4081
|
-
"gitDiff",
|
|
4082
|
-
"gitCreateBranch",
|
|
4083
|
-
"gitAdd",
|
|
4084
|
-
"gitCommit",
|
|
4085
|
-
"commitWorkspace"
|
|
4086
|
-
]);
|
|
4087
4306
|
function buildWorkspacePathProperty() {
|
|
4088
4307
|
return {
|
|
4089
4308
|
type: "string",
|
|
@@ -4327,7 +4546,7 @@ function buildWorkspaceToolDefinition(toolName) {
|
|
|
4327
4546
|
}
|
|
4328
4547
|
function filterDeclaredWorkspaceToolNames(args) {
|
|
4329
4548
|
return (args.toolNames ?? []).filter(
|
|
4330
|
-
(toolName) => WORKSPACE_TOOL_NAME_SET.has(toolName) &&
|
|
4549
|
+
(toolName) => WORKSPACE_TOOL_NAME_SET.has(toolName) && (args.exposeShellTools || !SHELL_TOOL_NAMES.includes(toolName))
|
|
4331
4550
|
);
|
|
4332
4551
|
}
|
|
4333
4552
|
|
|
@@ -4418,7 +4637,7 @@ function buildLocalWorkspacePolicyToolNames(args) {
|
|
|
4418
4637
|
toolNames: args.declaredToolNames,
|
|
4419
4638
|
exposeShellTools: args.exposeShellTools === true
|
|
4420
4639
|
})
|
|
4421
|
-
])]
|
|
4640
|
+
])];
|
|
4422
4641
|
}
|
|
4423
4642
|
function buildLocalWorkspaceOpenAiTools(args) {
|
|
4424
4643
|
const declaredTools = new Set(args.toolNames ?? []);
|
|
@@ -4489,7 +4708,6 @@ function readExpectedReplacementCount(args) {
|
|
|
4489
4708
|
return value;
|
|
4490
4709
|
}
|
|
4491
4710
|
var READ_FILE_SLICE_FALLBACK_LINES = 200;
|
|
4492
|
-
var LEGACY_SLICE_ARG_NAMES = ["startLine", "endLine", "maxLines", "tailLines"];
|
|
4493
4711
|
function isAbsentArg(value) {
|
|
4494
4712
|
return value === void 0 || value === null;
|
|
4495
4713
|
}
|
|
@@ -4559,46 +4777,16 @@ function parseLinesArg(value) {
|
|
|
4559
4777
|
}
|
|
4560
4778
|
function readFileSliceArgs(args) {
|
|
4561
4779
|
const warnings = [];
|
|
4562
|
-
const
|
|
4563
|
-
const withFallback = (slice) => {
|
|
4780
|
+
const withFallback = (slice2) => {
|
|
4564
4781
|
warnings.push(
|
|
4565
4782
|
`No usable line range remained; returned the first ${READ_FILE_SLICE_FALLBACK_LINES} lines instead of the whole file.`
|
|
4566
4783
|
);
|
|
4567
|
-
return { ...
|
|
4784
|
+
return { ...slice2, maxLines: READ_FILE_SLICE_FALLBACK_LINES, warnings };
|
|
4568
4785
|
};
|
|
4569
|
-
if (
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
const { slice, warning } = parseLinesArg(args.lines);
|
|
4574
|
-
if (warning) warnings.push(warning);
|
|
4575
|
-
return slice ? { ...slice, warnings } : withFallback({});
|
|
4576
|
-
}
|
|
4577
|
-
if (legacyUsed.length === 0) return { warnings };
|
|
4578
|
-
warnings.push(
|
|
4579
|
-
`${legacyUsed.join(", ")} ${legacyUsed.length > 1 ? "are" : "is"} deprecated; use lines instead (${LINES_ARG_SYNTAX}).`
|
|
4580
|
-
);
|
|
4581
|
-
const take = (name, max) => collectIntegerArg({ value: args[name], name, max, warnings });
|
|
4582
|
-
const resolved = {
|
|
4583
|
-
startLine: take("startLine"),
|
|
4584
|
-
endLine: take("endLine"),
|
|
4585
|
-
maxLines: take("maxLines", 2e3),
|
|
4586
|
-
tailLines: take("tailLines", 2e3)
|
|
4587
|
-
};
|
|
4588
|
-
if (resolved.endLine !== void 0 && resolved.startLine !== void 0 && resolved.endLine < resolved.startLine) {
|
|
4589
|
-
warnings.push(
|
|
4590
|
-
`Ignored endLine (${resolved.endLine}): endLine must be greater than or equal to startLine (${resolved.startLine}).`
|
|
4591
|
-
);
|
|
4592
|
-
resolved.endLine = void 0;
|
|
4593
|
-
}
|
|
4594
|
-
if (resolved.tailLines !== void 0 && (resolved.startLine !== void 0 || resolved.endLine !== void 0)) {
|
|
4595
|
-
warnings.push(
|
|
4596
|
-
`Ignored tailLines (${resolved.tailLines}): tailLines cannot be combined with startLine or endLine.`
|
|
4597
|
-
);
|
|
4598
|
-
resolved.tailLines = void 0;
|
|
4599
|
-
}
|
|
4600
|
-
const nothingUsable = LEGACY_SLICE_ARG_NAMES.every((name) => resolved[name] === void 0);
|
|
4601
|
-
return nothingUsable ? withFallback(resolved) : { ...resolved, warnings };
|
|
4786
|
+
if (isAbsentArg(args.lines)) return { warnings };
|
|
4787
|
+
const { slice, warning } = parseLinesArg(args.lines);
|
|
4788
|
+
if (warning) warnings.push(warning);
|
|
4789
|
+
return slice ? { ...slice, warnings } : withFallback({});
|
|
4602
4790
|
}
|
|
4603
4791
|
function buildReadFileSliceHint(totalLines) {
|
|
4604
4792
|
return `File has ${totalLines} lines. Use lines: ${LINES_ARG_SYNTAX}, or omit it for the whole file.`;
|