@cjhyy/code-shell-core 0.7.0-beta.1 → 0.7.0
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/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
- package/dist/cc-orchestrator/agent-adapter.js +4 -0
- package/dist/cc-orchestrator/codex-session-history.d.ts +14 -1
- package/dist/cc-orchestrator/codex-session-history.js +64 -4
- package/dist/cc-orchestrator/external-agent-changes.js +22 -5
- package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -1
- package/dist/cc-orchestrator/external-agent-driver.js +202 -38
- package/dist/cc-orchestrator/session-history.d.ts +35 -0
- package/dist/cc-orchestrator/session-history.js +96 -13
- package/dist/credentials/access.d.ts +1 -0
- package/dist/credentials/access.js +2 -0
- package/dist/credentials/index.d.ts +2 -1
- package/dist/credentials/index.js +1 -0
- package/dist/credentials/oauth.d.ts +20 -0
- package/dist/credentials/oauth.js +114 -0
- package/dist/credentials/store.d.ts +1 -0
- package/dist/credentials/store.js +3 -1
- package/dist/credentials/types.d.ts +47 -1
- package/dist/engine/engine.d.ts +6 -2
- package/dist/engine/engine.js +159 -192
- package/dist/engine/goal.d.ts +17 -0
- package/dist/engine/goal.js +16 -6
- package/dist/engine/input-attachments.js +156 -13
- package/dist/engine/run-image-input.d.ts +22 -0
- package/dist/engine/run-image-input.js +195 -0
- package/dist/engine/steer-queue.d.ts +3 -1
- package/dist/engine/steer-queue.js +10 -2
- package/dist/engine/turn-loop.d.ts +30 -1
- package/dist/engine/turn-loop.js +112 -17
- package/dist/hooks/goal-stop-hook.d.ts +33 -1
- package/dist/hooks/goal-stop-hook.js +202 -34
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/preset/index.js +14 -4
- package/dist/protocol/server.js +1 -1
- package/dist/protocol/types.d.ts +2 -0
- package/dist/session/session-manager.js +34 -1
- package/dist/tool-system/builtin/agent-notifications.d.ts +11 -4
- package/dist/tool-system/builtin/agent-notifications.js +19 -7
- package/dist/tool-system/builtin/background-jobs.d.ts +25 -5
- package/dist/tool-system/builtin/background-jobs.js +105 -7
- package/dist/tool-system/builtin/cron-list.definition.d.ts +3 -0
- package/dist/tool-system/builtin/cron-list.definition.js +6 -0
- package/dist/tool-system/builtin/cron.d.ts +1 -2
- package/dist/tool-system/builtin/cron.js +9 -7
- package/dist/tool-system/builtin/drive-claude-code.d.ts +7 -0
- package/dist/tool-system/builtin/drive-claude-code.js +307 -20
- package/dist/tool-system/builtin/index.js +15 -3
- package/dist/tool-system/builtin/sleep.d.ts +1 -2
- package/dist/tool-system/builtin/sleep.definition.d.ts +8 -0
- package/dist/tool-system/builtin/sleep.definition.js +28 -0
- package/dist/tool-system/builtin/sleep.js +1 -22
- package/dist/tool-system/context.d.ts +18 -0
- package/dist/tool-system/mcp-manager.d.ts +14 -2
- package/dist/tool-system/mcp-manager.js +56 -7
- package/dist/types.d.ts +23 -7
- package/package.json +1 -1
package/dist/engine/turn-loop.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { initialTurnState, newTurnId } from "./turn-state.js";
|
|
8
8
|
import { formatFriendlyError } from "./friendly-error.js";
|
|
9
|
+
import { projectGoalJudgeToolResult, } from "../hooks/goal-stop-hook.js";
|
|
9
10
|
import { wrapHookMessages } from "../hooks/inject.js";
|
|
10
11
|
import { ContextLimitError } from "../exceptions.js";
|
|
11
12
|
import { logger } from "../logging/logger.js";
|
|
@@ -19,7 +20,7 @@ import { COMPLETE_GOAL_TOOL_NAME } from "../tool-system/builtin/complete-goal.js
|
|
|
19
20
|
import { CANCEL_GOAL_TOOL_NAME } from "../tool-system/builtin/cancel-goal.js";
|
|
20
21
|
import { redactSensitiveToolResultsInMessages, toolResultForDisplay, toolResultTranscriptText, toolResultsForDisplay, } from "../tool-system/tool-result-redaction.js";
|
|
21
22
|
import { addTokenUsage, cacheHitRateFromUsage, cumulativeCacheHitRate, } from "./session-usage.js";
|
|
22
|
-
import { createGoalBudgetTracker, recordGoalUsage,
|
|
23
|
+
import { createGoalBudgetTracker, recordGoalUsage, goalBudgetTerminationReason, applyGoalExtension, limitProximity, GOAL_DEFAULT_MAX_STOP_BLOCKS, } from "./goal.js";
|
|
23
24
|
/**
|
|
24
25
|
* 把一个 ToolResult 映射成发给 LLM 的 tool_result ContentBlock。
|
|
25
26
|
* 有 contentBlocks(view_image 的图片块)就原样用作 content;否则
|
|
@@ -62,6 +63,7 @@ export class TurnLoop {
|
|
|
62
63
|
currentCumulativeUsage;
|
|
63
64
|
sensitiveToolResultRedactions = new Map();
|
|
64
65
|
pendingImageMessages = new Set();
|
|
66
|
+
volatileContextMessages = new Set();
|
|
65
67
|
/**
|
|
66
68
|
* Consecutive on_stop blocks (Goal mode kept the agent going). Reset to 0
|
|
67
69
|
* on any unblocked completion. When it reaches config.maxStopBlocks the
|
|
@@ -160,6 +162,9 @@ export class TurnLoop {
|
|
|
160
162
|
for (const msg of this.config.freshImageMessages ?? []) {
|
|
161
163
|
this.pendingImageMessages.add(msg);
|
|
162
164
|
}
|
|
165
|
+
for (const msg of this.config.volatileContextMessages ?? []) {
|
|
166
|
+
this.volatileContextMessages.add(msg);
|
|
167
|
+
}
|
|
163
168
|
// Wrap onStream so a single throwing handler can't silently break
|
|
164
169
|
// the channel for the rest of the run. A 2026-05-25 incident saw a
|
|
165
170
|
// sub-agent's events stop reaching the renderer ~23s into its run —
|
|
@@ -224,6 +229,23 @@ export class TurnLoop {
|
|
|
224
229
|
}
|
|
225
230
|
return result.messages;
|
|
226
231
|
}
|
|
232
|
+
stripVolatileContextMessages(messages) {
|
|
233
|
+
if (this.volatileContextMessages.size === 0)
|
|
234
|
+
return messages;
|
|
235
|
+
let changed = false;
|
|
236
|
+
const stripped = messages.filter((message) => {
|
|
237
|
+
const keep = !this.volatileContextMessages.has(message);
|
|
238
|
+
if (!keep)
|
|
239
|
+
changed = true;
|
|
240
|
+
return keep;
|
|
241
|
+
});
|
|
242
|
+
return changed ? stripped : messages;
|
|
243
|
+
}
|
|
244
|
+
appendVolatileContextMessages(messages) {
|
|
245
|
+
if (this.volatileContextMessages.size === 0)
|
|
246
|
+
return messages;
|
|
247
|
+
return [...this.stripVolatileContextMessages(messages), ...this.volatileContextMessages];
|
|
248
|
+
}
|
|
227
249
|
markPendingImagesConsumed(messages) {
|
|
228
250
|
if (this.pendingImageMessages.size === 0)
|
|
229
251
|
return messages;
|
|
@@ -307,6 +329,7 @@ export class TurnLoop {
|
|
|
307
329
|
recordResponseUsage(usage) {
|
|
308
330
|
this.currentTurnUsage = addTokenUsage(this.currentTurnUsage, usage);
|
|
309
331
|
this.currentCumulativeUsage = this.deps.recordCumulativeUsage?.(usage);
|
|
332
|
+
this.deps.recordCacheReadDiagnostics?.(usage);
|
|
310
333
|
}
|
|
311
334
|
emitCtxFromUsage(usage, messages) {
|
|
312
335
|
if (!this.config.onStream)
|
|
@@ -379,6 +402,10 @@ export class TurnLoop {
|
|
|
379
402
|
let messages = [...initialMessages];
|
|
380
403
|
let finalText = "";
|
|
381
404
|
const budgetTracker = createBudgetTracker();
|
|
405
|
+
// Recent execution evidence for the built-in Goal judge. Values enter this
|
|
406
|
+
// window only after irreversible sensitive-data removal, non-text omission,
|
|
407
|
+
// and per-item truncation at the current-round ToolResult boundary.
|
|
408
|
+
const goalToolResults = [];
|
|
382
409
|
// Goal-mode run-scoped budget tracker (P0). Null when no goal. Stamps a
|
|
383
410
|
// wall-clock start now and accumulates prompt+completion tokens across
|
|
384
411
|
// every turn; the guardrail below force-stops the run once any configured
|
|
@@ -425,7 +452,7 @@ export class TurnLoop {
|
|
|
425
452
|
// they join THIS step's request — no abort, no lost in-flight work. Same
|
|
426
453
|
// loop-top user-push pattern as turnStartInjection / turn-limit warnings
|
|
427
454
|
// below. Push to transcript too so they persist + survive resume.
|
|
428
|
-
this.consumeQueuedSteer(messages, "normal_step");
|
|
455
|
+
await this.consumeQueuedSteer(messages, "normal_step");
|
|
429
456
|
const state = initialTurnState(this.turnCount);
|
|
430
457
|
// Per-turn correlation ID. Every log written through `tlog` (or any
|
|
431
458
|
// child derived from it) is stamped with `turn` + `turnId`, so
|
|
@@ -487,6 +514,7 @@ export class TurnLoop {
|
|
|
487
514
|
// pendingImageMessages are preserved through this next model request.
|
|
488
515
|
const hasPendingSensitiveToolResults = this.sensitiveToolResultRedactions.size > 0;
|
|
489
516
|
messages = this.prepareMessagesForModel(messages);
|
|
517
|
+
messages = this.stripVolatileContextMessages(messages);
|
|
490
518
|
if (hasPendingSensitiveToolResults) {
|
|
491
519
|
tlog.info("turn.sensitive_tool_result_context_management_skipped", {
|
|
492
520
|
cat: "turn",
|
|
@@ -531,6 +559,7 @@ export class TurnLoop {
|
|
|
531
559
|
messages.push(compactInjection);
|
|
532
560
|
}
|
|
533
561
|
}
|
|
562
|
+
messages = this.appendVolatileContextMessages(messages);
|
|
534
563
|
// Model call (with streaming fallback and max_output_tokens continuation)
|
|
535
564
|
// Track tool IDs streamed during this turn to avoid duplicate UI events
|
|
536
565
|
this.streamedToolIds.clear();
|
|
@@ -602,7 +631,8 @@ export class TurnLoop {
|
|
|
602
631
|
// compaction decisions use hybrid (actual + delta) estimation rather than
|
|
603
632
|
// pure heuristics. Without this the manager falls back to char/4 estimates.
|
|
604
633
|
if (response.usage?.promptTokens !== undefined) {
|
|
605
|
-
const
|
|
634
|
+
const contextAnchorMessages = this.stripVolatileContextMessages(messages);
|
|
635
|
+
const anchor = this.deps.contextManager.recordActualUsage(response.usage.promptTokens, contextAnchorMessages.length, contextAnchorMessages);
|
|
606
636
|
if (anchor)
|
|
607
637
|
this.deps.recordContextUsageAnchor?.(anchor);
|
|
608
638
|
}
|
|
@@ -692,7 +722,10 @@ export class TurnLoop {
|
|
|
692
722
|
// is force-stopped regardless of what the model wants to do next (stop OR
|
|
693
723
|
// continue with tool calls). This is the unattended-safety backstop, so
|
|
694
724
|
// it sits BEFORE the "tool calls?" branch — both paths pass the gate.
|
|
695
|
-
|
|
725
|
+
const budgetTermination = goalTracker
|
|
726
|
+
? goalBudgetTerminationReason(goalTracker, Date.now())
|
|
727
|
+
: undefined;
|
|
728
|
+
if (goalTracker && budgetTermination) {
|
|
696
729
|
tlog.info("turn.goal_budget_exhausted", {
|
|
697
730
|
cat: "goal",
|
|
698
731
|
tokensUsed: goalTracker.tokensUsed,
|
|
@@ -707,7 +740,12 @@ export class TurnLoop {
|
|
|
707
740
|
content: "(Goal 预算已耗尽,强制停止。)",
|
|
708
741
|
},
|
|
709
742
|
});
|
|
710
|
-
return {
|
|
743
|
+
return {
|
|
744
|
+
text: finalText,
|
|
745
|
+
reason: "goal_budget_exhausted",
|
|
746
|
+
messages,
|
|
747
|
+
goalTermination: budgetTermination,
|
|
748
|
+
};
|
|
711
749
|
}
|
|
712
750
|
// Post-check: tool calls?
|
|
713
751
|
if (response.toolCalls.length === 0) {
|
|
@@ -722,7 +760,7 @@ export class TurnLoop {
|
|
|
722
760
|
hasToolUse: false,
|
|
723
761
|
});
|
|
724
762
|
messages.push({ role: "assistant", content: finalText });
|
|
725
|
-
if (this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
763
|
+
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
726
764
|
continue;
|
|
727
765
|
}
|
|
728
766
|
// on_stop seam: the model wants to stop. Give handlers (Goal mode)
|
|
@@ -731,6 +769,23 @@ export class TurnLoop {
|
|
|
731
769
|
// we run another turn instead of returning. Bounded by
|
|
732
770
|
// maxStopBlocks (consecutive) and the outer maxTurns ceiling.
|
|
733
771
|
const maxStopBlocks = this.config.maxStopBlocks ?? GOAL_DEFAULT_MAX_STOP_BLOCKS;
|
|
772
|
+
if (goalTracker) {
|
|
773
|
+
this.deps.updateGoalJudgeContext?.({
|
|
774
|
+
toolResults: goalToolResults.map((item) => ({
|
|
775
|
+
...item,
|
|
776
|
+
})),
|
|
777
|
+
progress: {
|
|
778
|
+
turnCount: this.turnCount,
|
|
779
|
+
stopRound: this.stopBlockCount + 1,
|
|
780
|
+
elapsedMs: Math.max(0, Date.now() - goalTracker.startedAtMs),
|
|
781
|
+
tokensUsed: goalTracker.tokensUsed,
|
|
782
|
+
tokenBudget: goalTracker.goal.tokenBudget,
|
|
783
|
+
timeBudgetMs: goalTracker.goal.timeBudgetMs,
|
|
784
|
+
maxTurns: this.config.maxTurns,
|
|
785
|
+
maxStopBlocks,
|
|
786
|
+
},
|
|
787
|
+
});
|
|
788
|
+
}
|
|
734
789
|
const stopHook = await this.emitHook("on_stop", {
|
|
735
790
|
goal: this.config.goal,
|
|
736
791
|
finalText,
|
|
@@ -775,6 +830,7 @@ export class TurnLoop {
|
|
|
775
830
|
});
|
|
776
831
|
continue;
|
|
777
832
|
}
|
|
833
|
+
let goalTermination;
|
|
778
834
|
if (stopHook.continueSession && this.stopBlockCount >= maxStopBlocks) {
|
|
779
835
|
// Cap hit: stop anyway, but tell the user why we're not looping
|
|
780
836
|
// forever on an unsatisfiable goal.
|
|
@@ -788,6 +844,7 @@ export class TurnLoop {
|
|
|
788
844
|
status: "exhausted",
|
|
789
845
|
round: this.stopBlockCount,
|
|
790
846
|
});
|
|
847
|
+
goalTermination = "stop_blocks_exhausted";
|
|
791
848
|
this.config.onStream?.({
|
|
792
849
|
type: "assistant_message",
|
|
793
850
|
messageId: assistantMessageId,
|
|
@@ -807,11 +864,11 @@ export class TurnLoop {
|
|
|
807
864
|
});
|
|
808
865
|
}
|
|
809
866
|
this.stopBlockCount = 0;
|
|
810
|
-
if (this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
867
|
+
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
811
868
|
continue;
|
|
812
869
|
}
|
|
813
870
|
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
814
|
-
return { text: finalText, reason: "completed", messages };
|
|
871
|
+
return { text: finalText, reason: "completed", messages, goalTermination };
|
|
815
872
|
}
|
|
816
873
|
// Tool execution phase
|
|
817
874
|
tlog.info("turn.tool_use", {
|
|
@@ -854,6 +911,13 @@ export class TurnLoop {
|
|
|
854
911
|
// Record results in transcript and stream
|
|
855
912
|
const resultBlocks = [];
|
|
856
913
|
for (const result of results) {
|
|
914
|
+
if (goalTracker) {
|
|
915
|
+
goalToolResults.push(projectGoalJudgeToolResult(result, this.turnCount));
|
|
916
|
+
// The prompt renderer keeps at most 12, but retaining a few extra
|
|
917
|
+
// bounded projections lets its total-char budget choose the newest useful subset.
|
|
918
|
+
if (goalToolResults.length > 20)
|
|
919
|
+
goalToolResults.splice(0, goalToolResults.length - 20);
|
|
920
|
+
}
|
|
857
921
|
resultBlocks.push(toolResultToBlock(result));
|
|
858
922
|
const streamResult = toolResultForDisplay(result);
|
|
859
923
|
const transcriptResult = toolResultTranscriptText(result);
|
|
@@ -930,7 +994,7 @@ export class TurnLoop {
|
|
|
930
994
|
tlog.info("turn.goal_self_reported_complete", { cat: "goal" });
|
|
931
995
|
this.stopBlockCount = 0;
|
|
932
996
|
this.deps.clearPersistedGoal?.();
|
|
933
|
-
if (this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
997
|
+
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
934
998
|
continue;
|
|
935
999
|
}
|
|
936
1000
|
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
@@ -946,7 +1010,7 @@ export class TurnLoop {
|
|
|
946
1010
|
tlog.info("turn.goal_user_cancelled", { cat: "goal" });
|
|
947
1011
|
this.stopBlockCount = 0;
|
|
948
1012
|
this.deps.clearPersistedGoal?.();
|
|
949
|
-
if (this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
1013
|
+
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
950
1014
|
continue;
|
|
951
1015
|
}
|
|
952
1016
|
return { text: finalText, reason: "completed", messages };
|
|
@@ -966,7 +1030,7 @@ export class TurnLoop {
|
|
|
966
1030
|
message: { role: "assistant", content: finalText },
|
|
967
1031
|
});
|
|
968
1032
|
messages.push({ role: "assistant", content: finalText });
|
|
969
|
-
if (this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
1033
|
+
if (await this.consumeQueuedSteer(messages, "finalize_backfill")) {
|
|
970
1034
|
continue;
|
|
971
1035
|
}
|
|
972
1036
|
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
@@ -1048,7 +1112,7 @@ export class TurnLoop {
|
|
|
1048
1112
|
maxTurns: this.config.maxTurns,
|
|
1049
1113
|
turnCount: this.turnCount,
|
|
1050
1114
|
});
|
|
1051
|
-
this.consumeQueuedSteer(messages, "finalize_backfill");
|
|
1115
|
+
await this.consumeQueuedSteer(messages, "finalize_backfill");
|
|
1052
1116
|
const hasPendingSensitiveToolResults = this.sensitiveToolResultRedactions.size > 0;
|
|
1053
1117
|
messages = this.prepareMessagesForModel(messages);
|
|
1054
1118
|
if (hasPendingSensitiveToolResults) {
|
|
@@ -1086,7 +1150,12 @@ export class TurnLoop {
|
|
|
1086
1150
|
messages.push({ role: "assistant", content: finalText });
|
|
1087
1151
|
}
|
|
1088
1152
|
messages = this.redactConsumedSensitiveToolResults(messages);
|
|
1089
|
-
return {
|
|
1153
|
+
return {
|
|
1154
|
+
text: finalText,
|
|
1155
|
+
reason: "max_turns",
|
|
1156
|
+
messages,
|
|
1157
|
+
...(this.config.goal ? { goalTermination: "max_turns_exhausted" } : {}),
|
|
1158
|
+
};
|
|
1090
1159
|
}
|
|
1091
1160
|
/**
|
|
1092
1161
|
* Call model with streaming fallback.
|
|
@@ -1168,10 +1237,12 @@ export class TurnLoop {
|
|
|
1168
1237
|
get currentTurn() {
|
|
1169
1238
|
return this.turnCount;
|
|
1170
1239
|
}
|
|
1171
|
-
consumeQueuedSteer(messages, source) {
|
|
1240
|
+
async consumeQueuedSteer(messages, source) {
|
|
1172
1241
|
const steered = this.deps.consumeSteer?.(source) ?? [];
|
|
1173
1242
|
let consumed = false;
|
|
1174
|
-
for (
|
|
1243
|
+
for (let index = 0; index < steered.length; index++) {
|
|
1244
|
+
const item = steered[index];
|
|
1245
|
+
const { id, text, clientMessageId } = item;
|
|
1175
1246
|
if (!text)
|
|
1176
1247
|
continue;
|
|
1177
1248
|
if (clientMessageId && this.deps.claimClientMessageId?.(clientMessageId, "steer") === false) {
|
|
@@ -1183,9 +1254,33 @@ export class TurnLoop {
|
|
|
1183
1254
|
});
|
|
1184
1255
|
continue;
|
|
1185
1256
|
}
|
|
1257
|
+
let content;
|
|
1258
|
+
try {
|
|
1259
|
+
content = this.deps.buildSteerUserMessageContent
|
|
1260
|
+
? await this.deps.buildSteerUserMessageContent(item)
|
|
1261
|
+
: text;
|
|
1262
|
+
}
|
|
1263
|
+
catch (err) {
|
|
1264
|
+
if (clientMessageId)
|
|
1265
|
+
this.deps.releaseClientMessageId?.(clientMessageId);
|
|
1266
|
+
const pendingSuffix = steered.slice(index);
|
|
1267
|
+
this.deps.restoreSteer?.(pendingSuffix);
|
|
1268
|
+
logger.warn("steer.prepare.failed_requeued", {
|
|
1269
|
+
clientMessageId,
|
|
1270
|
+
steerId: id,
|
|
1271
|
+
sessionId: this.deps.sessionId,
|
|
1272
|
+
source,
|
|
1273
|
+
restoredCount: pendingSuffix.length,
|
|
1274
|
+
error: err.message,
|
|
1275
|
+
});
|
|
1276
|
+
break;
|
|
1277
|
+
}
|
|
1186
1278
|
consumed = true;
|
|
1187
|
-
|
|
1188
|
-
|
|
1279
|
+
this.deps.setOriginClientMessageId?.(clientMessageId);
|
|
1280
|
+
const message = { role: "user", content };
|
|
1281
|
+
messages.push(message);
|
|
1282
|
+
this.trackFreshImageMessage(message);
|
|
1283
|
+
this.deps.transcript.appendMessage("user", content, { steerId: id, clientMessageId });
|
|
1189
1284
|
this.config.onStream?.({ type: "steer_injected", text, id });
|
|
1190
1285
|
}
|
|
1191
1286
|
return consumed;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HookHandler } from "./registry.js";
|
|
2
|
-
import type { LLMResponse } from "../types.js";
|
|
2
|
+
import type { LLMResponse, ToolResult } from "../types.js";
|
|
3
3
|
import { type GoalConfig } from "../engine/goal.js";
|
|
4
4
|
/** Narrow LLM surface the judge needs — just a one-shot completion. */
|
|
5
5
|
export interface GoalJudgeLLM {
|
|
@@ -53,6 +53,38 @@ export interface GoalStopHookOptions {
|
|
|
53
53
|
* `() => new Date()`.
|
|
54
54
|
*/
|
|
55
55
|
now?: () => Date;
|
|
56
|
+
/**
|
|
57
|
+
* Private runtime evidence supplied by TurnLoop immediately before on_stop.
|
|
58
|
+
* It deliberately lives on this closure rather than HookContext.data so
|
|
59
|
+
* third-party/public stop hooks do not gain access to tool output.
|
|
60
|
+
*/
|
|
61
|
+
getJudgeContext?: () => GoalJudgeRuntimeContext | undefined;
|
|
62
|
+
}
|
|
63
|
+
export interface GoalJudgeRuntimeContext {
|
|
64
|
+
/** Recent irreversible projections from this run, newest evidence retained by TurnLoop. */
|
|
65
|
+
toolResults: GoalJudgeToolResult[];
|
|
66
|
+
progress: {
|
|
67
|
+
turnCount: number;
|
|
68
|
+
/** One-based natural-stop/judge round for this run. */
|
|
69
|
+
stopRound: number;
|
|
70
|
+
elapsedMs: number;
|
|
71
|
+
tokensUsed: number;
|
|
72
|
+
tokenBudget?: number;
|
|
73
|
+
timeBudgetMs?: number;
|
|
74
|
+
maxTurns?: number;
|
|
75
|
+
maxStopBlocks?: number;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export interface GoalJudgeToolResult {
|
|
79
|
+
turnCount: number;
|
|
80
|
+
toolName: string;
|
|
81
|
+
status: "success" | "error";
|
|
82
|
+
/** Bounded plain text only; absent for sensitive or purely non-text results. */
|
|
83
|
+
text?: string;
|
|
84
|
+
/** True when image/binary/structured blocks were replaced with a placeholder. */
|
|
85
|
+
omittedNonText?: true;
|
|
56
86
|
}
|
|
87
|
+
/** Build the bounded, irreversible value retained beyond the current model round. */
|
|
88
|
+
export declare function projectGoalJudgeToolResult(result: ToolResult, turnCount: number): GoalJudgeToolResult;
|
|
57
89
|
export declare function createGoalStopHook(opts: GoalStopHookOptions): HookHandler;
|
|
58
90
|
export {};
|