@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.
Files changed (57) hide show
  1. package/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
  2. package/dist/cc-orchestrator/agent-adapter.js +4 -0
  3. package/dist/cc-orchestrator/codex-session-history.d.ts +14 -1
  4. package/dist/cc-orchestrator/codex-session-history.js +64 -4
  5. package/dist/cc-orchestrator/external-agent-changes.js +22 -5
  6. package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -1
  7. package/dist/cc-orchestrator/external-agent-driver.js +202 -38
  8. package/dist/cc-orchestrator/session-history.d.ts +35 -0
  9. package/dist/cc-orchestrator/session-history.js +96 -13
  10. package/dist/credentials/access.d.ts +1 -0
  11. package/dist/credentials/access.js +2 -0
  12. package/dist/credentials/index.d.ts +2 -1
  13. package/dist/credentials/index.js +1 -0
  14. package/dist/credentials/oauth.d.ts +20 -0
  15. package/dist/credentials/oauth.js +114 -0
  16. package/dist/credentials/store.d.ts +1 -0
  17. package/dist/credentials/store.js +3 -1
  18. package/dist/credentials/types.d.ts +47 -1
  19. package/dist/engine/engine.d.ts +6 -2
  20. package/dist/engine/engine.js +159 -192
  21. package/dist/engine/goal.d.ts +17 -0
  22. package/dist/engine/goal.js +16 -6
  23. package/dist/engine/input-attachments.js +156 -13
  24. package/dist/engine/run-image-input.d.ts +22 -0
  25. package/dist/engine/run-image-input.js +195 -0
  26. package/dist/engine/steer-queue.d.ts +3 -1
  27. package/dist/engine/steer-queue.js +10 -2
  28. package/dist/engine/turn-loop.d.ts +30 -1
  29. package/dist/engine/turn-loop.js +112 -17
  30. package/dist/hooks/goal-stop-hook.d.ts +33 -1
  31. package/dist/hooks/goal-stop-hook.js +202 -34
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +2 -2
  34. package/dist/preset/index.js +14 -4
  35. package/dist/protocol/server.js +1 -1
  36. package/dist/protocol/types.d.ts +2 -0
  37. package/dist/session/session-manager.js +34 -1
  38. package/dist/tool-system/builtin/agent-notifications.d.ts +11 -4
  39. package/dist/tool-system/builtin/agent-notifications.js +19 -7
  40. package/dist/tool-system/builtin/background-jobs.d.ts +25 -5
  41. package/dist/tool-system/builtin/background-jobs.js +105 -7
  42. package/dist/tool-system/builtin/cron-list.definition.d.ts +3 -0
  43. package/dist/tool-system/builtin/cron-list.definition.js +6 -0
  44. package/dist/tool-system/builtin/cron.d.ts +1 -2
  45. package/dist/tool-system/builtin/cron.js +9 -7
  46. package/dist/tool-system/builtin/drive-claude-code.d.ts +7 -0
  47. package/dist/tool-system/builtin/drive-claude-code.js +307 -20
  48. package/dist/tool-system/builtin/index.js +15 -3
  49. package/dist/tool-system/builtin/sleep.d.ts +1 -2
  50. package/dist/tool-system/builtin/sleep.definition.d.ts +8 -0
  51. package/dist/tool-system/builtin/sleep.definition.js +28 -0
  52. package/dist/tool-system/builtin/sleep.js +1 -22
  53. package/dist/tool-system/context.d.ts +18 -0
  54. package/dist/tool-system/mcp-manager.d.ts +14 -2
  55. package/dist/tool-system/mcp-manager.js +56 -7
  56. package/dist/types.d.ts +23 -7
  57. package/package.json +1 -1
@@ -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, goalBudgetExceeded, applyGoalExtension, limitProximity, GOAL_DEFAULT_MAX_STOP_BLOCKS, } from "./goal.js";
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 anchor = this.deps.contextManager.recordActualUsage(response.usage.promptTokens, messages.length, messages);
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
- if (goalTracker && goalBudgetExceeded(goalTracker, Date.now())) {
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 { text: finalText, reason: "goal_budget_exhausted", messages };
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 { text: finalText, reason: "max_turns", messages };
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 (const { id, text, clientMessageId } of steered) {
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
- messages.push({ role: "user", content: text });
1188
- this.deps.transcript.appendMessage("user", text, { steerId: id, clientMessageId });
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 {};