@jungjaehoon/mama-os 0.32.1 → 0.32.2

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 (182) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +15 -13
  3. package/dist/agent/agent-loop.d.ts +5 -0
  4. package/dist/agent/agent-loop.d.ts.map +1 -1
  5. package/dist/agent/agent-loop.js +160 -55
  6. package/dist/agent/agent-loop.js.map +1 -1
  7. package/dist/agent/backend-model-policy.d.ts +9 -0
  8. package/dist/agent/backend-model-policy.d.ts.map +1 -0
  9. package/dist/agent/backend-model-policy.js +21 -0
  10. package/dist/agent/backend-model-policy.js.map +1 -0
  11. package/dist/agent/backend-model-runner-factory.d.ts +27 -0
  12. package/dist/agent/backend-model-runner-factory.d.ts.map +1 -0
  13. package/dist/agent/backend-model-runner-factory.js +85 -0
  14. package/dist/agent/backend-model-runner-factory.js.map +1 -0
  15. package/dist/agent/cline-cli-adapter.d.ts +124 -0
  16. package/dist/agent/cline-cli-adapter.d.ts.map +1 -0
  17. package/dist/agent/cline-cli-adapter.js +1040 -0
  18. package/dist/agent/cline-cli-adapter.js.map +1 -0
  19. package/dist/agent/cline-native-tool-policy.d.ts +3 -0
  20. package/dist/agent/cline-native-tool-policy.d.ts.map +1 -0
  21. package/dist/agent/cline-native-tool-policy.js +50 -0
  22. package/dist/agent/cline-native-tool-policy.js.map +1 -0
  23. package/dist/agent/code-act/constants.d.ts +1 -1
  24. package/dist/agent/code-act/constants.d.ts.map +1 -1
  25. package/dist/agent/code-act/constants.js +13 -1
  26. package/dist/agent/code-act/constants.js.map +1 -1
  27. package/dist/agent/codex-app-server-process.d.ts +5 -0
  28. package/dist/agent/codex-app-server-process.d.ts.map +1 -1
  29. package/dist/agent/codex-app-server-process.js +30 -0
  30. package/dist/agent/codex-app-server-process.js.map +1 -1
  31. package/dist/agent/codex-auxiliary-tools.d.ts +17 -0
  32. package/dist/agent/codex-auxiliary-tools.d.ts.map +1 -0
  33. package/dist/agent/codex-auxiliary-tools.js +267 -0
  34. package/dist/agent/codex-auxiliary-tools.js.map +1 -0
  35. package/dist/agent/codex-home.d.ts.map +1 -1
  36. package/dist/agent/codex-home.js +1 -0
  37. package/dist/agent/codex-home.js.map +1 -1
  38. package/dist/agent/managed-agent-runtime-sync.d.ts.map +1 -1
  39. package/dist/agent/managed-agent-runtime-sync.js +17 -3
  40. package/dist/agent/managed-agent-runtime-sync.js.map +1 -1
  41. package/dist/agent/managed-agent-validation.js +1 -1
  42. package/dist/agent/managed-agent-validation.js.map +1 -1
  43. package/dist/agent/model-runner.d.ts +11 -1
  44. package/dist/agent/model-runner.d.ts.map +1 -1
  45. package/dist/agent/model-runner.js +12 -1
  46. package/dist/agent/model-runner.js.map +1 -1
  47. package/dist/agent/session-pool.d.ts +10 -9
  48. package/dist/agent/session-pool.d.ts.map +1 -1
  49. package/dist/agent/session-pool.js +12 -47
  50. package/dist/agent/session-pool.js.map +1 -1
  51. package/dist/agent/types.d.ts +18 -2
  52. package/dist/agent/types.d.ts.map +1 -1
  53. package/dist/agent/types.js.map +1 -1
  54. package/dist/api/graph-api.d.ts.map +1 -1
  55. package/dist/api/graph-api.js +15 -5
  56. package/dist/api/graph-api.js.map +1 -1
  57. package/dist/cli/commands/init.d.ts +1 -1
  58. package/dist/cli/commands/init.d.ts.map +1 -1
  59. package/dist/cli/commands/init.js +43 -10
  60. package/dist/cli/commands/init.js.map +1 -1
  61. package/dist/cli/commands/run.d.ts.map +1 -1
  62. package/dist/cli/commands/run.js +12 -0
  63. package/dist/cli/commands/run.js.map +1 -1
  64. package/dist/cli/commands/setup.d.ts +22 -0
  65. package/dist/cli/commands/setup.d.ts.map +1 -1
  66. package/dist/cli/commands/setup.js +79 -28
  67. package/dist/cli/commands/setup.js.map +1 -1
  68. package/dist/cli/commands/start.d.ts +2 -1
  69. package/dist/cli/commands/start.d.ts.map +1 -1
  70. package/dist/cli/commands/start.js +35 -9
  71. package/dist/cli/commands/start.js.map +1 -1
  72. package/dist/cli/commands/status.d.ts.map +1 -1
  73. package/dist/cli/commands/status.js +4 -0
  74. package/dist/cli/commands/status.js.map +1 -1
  75. package/dist/cli/config/config-manager.d.ts.map +1 -1
  76. package/dist/cli/config/config-manager.js +63 -14
  77. package/dist/cli/config/config-manager.js.map +1 -1
  78. package/dist/cli/config/types.d.ts +12 -5
  79. package/dist/cli/config/types.d.ts.map +1 -1
  80. package/dist/cli/config/types.js +3 -0
  81. package/dist/cli/config/types.js.map +1 -1
  82. package/dist/cli/index.js +6 -4
  83. package/dist/cli/index.js.map +1 -1
  84. package/dist/cli/runtime/agent-loop-init.d.ts +1 -1
  85. package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
  86. package/dist/cli/runtime/agent-loop-init.js +8 -3
  87. package/dist/cli/runtime/agent-loop-init.js.map +1 -1
  88. package/dist/cli/runtime/gateway-init.d.ts +1 -1
  89. package/dist/cli/runtime/gateway-init.d.ts.map +1 -1
  90. package/dist/cli/runtime/gateway-init.js.map +1 -1
  91. package/dist/cli/runtime/mama-core-init.d.ts +10 -2
  92. package/dist/cli/runtime/mama-core-init.d.ts.map +1 -1
  93. package/dist/cli/runtime/mama-core-init.js +52 -68
  94. package/dist/cli/runtime/mama-core-init.js.map +1 -1
  95. package/dist/cli/runtime/memory-agent-init.d.ts.map +1 -1
  96. package/dist/cli/runtime/memory-agent-init.js +18 -5
  97. package/dist/cli/runtime/memory-agent-init.js.map +1 -1
  98. package/dist/cli/runtime/scheduler-init.d.ts +4 -0
  99. package/dist/cli/runtime/scheduler-init.d.ts.map +1 -1
  100. package/dist/cli/runtime/scheduler-init.js +24 -3
  101. package/dist/cli/runtime/scheduler-init.js.map +1 -1
  102. package/dist/cli/runtime/shutdown.d.ts +4 -0
  103. package/dist/cli/runtime/shutdown.d.ts.map +1 -1
  104. package/dist/cli/runtime/shutdown.js +30 -6
  105. package/dist/cli/runtime/shutdown.js.map +1 -1
  106. package/dist/cli/runtime/utilities.d.ts +2 -0
  107. package/dist/cli/runtime/utilities.d.ts.map +1 -1
  108. package/dist/cli/runtime/utilities.js +33 -0
  109. package/dist/cli/runtime/utilities.js.map +1 -1
  110. package/dist/gateways/discord.d.ts.map +1 -1
  111. package/dist/gateways/discord.js +11 -6
  112. package/dist/gateways/discord.js.map +1 -1
  113. package/dist/gateways/image-analyzer.d.ts +5 -0
  114. package/dist/gateways/image-analyzer.d.ts.map +1 -1
  115. package/dist/gateways/image-analyzer.js +8 -0
  116. package/dist/gateways/image-analyzer.js.map +1 -1
  117. package/dist/gateways/message-router.d.ts.map +1 -1
  118. package/dist/gateways/message-router.js +29 -15
  119. package/dist/gateways/message-router.js.map +1 -1
  120. package/dist/gateways/slack.d.ts.map +1 -1
  121. package/dist/gateways/slack.js +6 -2
  122. package/dist/gateways/slack.js.map +1 -1
  123. package/dist/gateways/types.d.ts +1 -1
  124. package/dist/gateways/types.d.ts.map +1 -1
  125. package/dist/index.d.ts +1 -1
  126. package/dist/index.js +1 -1
  127. package/dist/multi-agent/agent-process-manager.d.ts +6 -4
  128. package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
  129. package/dist/multi-agent/agent-process-manager.js +103 -34
  130. package/dist/multi-agent/agent-process-manager.js.map +1 -1
  131. package/dist/multi-agent/runtime-process.d.ts +3 -0
  132. package/dist/multi-agent/runtime-process.d.ts.map +1 -1
  133. package/dist/multi-agent/runtime-process.js +10 -2
  134. package/dist/multi-agent/runtime-process.js.map +1 -1
  135. package/dist/multi-agent/types.d.ts +12 -2
  136. package/dist/multi-agent/types.d.ts.map +1 -1
  137. package/dist/multi-agent/types.js.map +1 -1
  138. package/dist/multi-agent/workflow-types.d.ts +1 -1
  139. package/dist/multi-agent/workflow-types.d.ts.map +1 -1
  140. package/dist/onboarding/complete-autonomous-prompt.d.ts +1 -1
  141. package/dist/onboarding/complete-autonomous-prompt.d.ts.map +1 -1
  142. package/dist/onboarding/complete-autonomous-prompt.js +9 -9
  143. package/dist/operator/situation-report.d.ts.map +1 -1
  144. package/dist/operator/situation-report.js +38 -23
  145. package/dist/operator/situation-report.js.map +1 -1
  146. package/dist/operator/temporal-runtime.d.ts.map +1 -1
  147. package/dist/operator/temporal-runtime.js +3 -1
  148. package/dist/operator/temporal-runtime.js.map +1 -1
  149. package/dist/operator/trigger-author.d.ts +13 -1
  150. package/dist/operator/trigger-author.d.ts.map +1 -1
  151. package/dist/operator/trigger-author.js +35 -0
  152. package/dist/operator/trigger-author.js.map +1 -1
  153. package/dist/operator/worker-run.d.ts.map +1 -1
  154. package/dist/operator/worker-run.js +10 -4
  155. package/dist/operator/worker-run.js.map +1 -1
  156. package/dist/scheduler/cron-worker.d.ts +8 -4
  157. package/dist/scheduler/cron-worker.d.ts.map +1 -1
  158. package/dist/scheduler/cron-worker.js +56 -39
  159. package/dist/scheduler/cron-worker.js.map +1 -1
  160. package/dist/setup/setup-actions.d.ts +26 -0
  161. package/dist/setup/setup-actions.d.ts.map +1 -0
  162. package/dist/setup/setup-actions.js +371 -0
  163. package/dist/setup/setup-actions.js.map +1 -0
  164. package/dist/setup/setup-prompt.d.ts +1 -1
  165. package/dist/setup/setup-prompt.d.ts.map +1 -1
  166. package/dist/setup/setup-prompt.js +28 -33
  167. package/dist/setup/setup-prompt.js.map +1 -1
  168. package/dist/setup/setup-server.d.ts +6 -1
  169. package/dist/setup/setup-server.d.ts.map +1 -1
  170. package/dist/setup/setup-server.js +55 -9
  171. package/dist/setup/setup-server.js.map +1 -1
  172. package/dist/setup/setup-websocket.d.ts +14 -2
  173. package/dist/setup/setup-websocket.d.ts.map +1 -1
  174. package/dist/setup/setup-websocket.js +121 -37
  175. package/dist/setup/setup-websocket.js.map +1 -1
  176. package/package.json +1 -1
  177. package/public/setup.html +9 -6
  178. package/public/viewer/js/modules/agents.js +5 -4
  179. package/public/viewer/js/modules/settings.js +34 -10
  180. package/public/viewer/src/modules/agents.ts +5 -4
  181. package/public/viewer/src/modules/settings.ts +49 -11
  182. package/public/viewer/src/utils/api.ts +3 -2
@@ -54,6 +54,8 @@ const fs_1 = require("fs");
54
54
  const prompt_size_monitor_js_1 = require("./prompt-size-monitor.js");
55
55
  const skill_loader_js_1 = require("./skill-loader.js");
56
56
  const persistent_cli_adapter_js_1 = require("./persistent-cli-adapter.js");
57
+ const cline_cli_adapter_js_1 = require("./cline-cli-adapter.js");
58
+ const cline_native_tool_policy_js_1 = require("./cline-native-tool-policy.js");
57
59
  const runtime_process_js_1 = require("../multi-agent/runtime-process.js");
58
60
  const model_runner_js_1 = require("./model-runner.js");
59
61
  const gateway_tool_executor_js_1 = require("./gateway-tool-executor.js");
@@ -75,6 +77,14 @@ const pre_compact_handler_js_1 = require("./pre-compact-handler.js");
75
77
  const debugLogger = __importStar(require("@jungjaehoon/mama-core/debug-logger"));
76
78
  const { DebugLogger } = debugLogger;
77
79
  const logger = new DebugLogger('AgentLoop');
80
+ const MODEL_RUNNER_AGENT_ERROR_CODES = {
81
+ timeout: 'NETWORK_ERROR',
82
+ crash: 'CLI_ERROR',
83
+ context_overflow: 'MAX_TOKENS',
84
+ auth_failure: 'AUTH_ERROR',
85
+ rate_limit: 'RATE_LIMIT',
86
+ unknown: 'CLI_ERROR',
87
+ };
78
88
  /**
79
89
  * Default configuration
80
90
  */
@@ -506,6 +516,11 @@ class AgentLoop {
506
516
  // overlapping owner chat) would steal each other's callbacks and leak tiers
507
517
  // if these were instance fields.
508
518
  disallowedTools;
519
+ clineNativeAllowedTools;
520
+ clineNativeDisallowedTools;
521
+ clineNativePolicyConfigured;
522
+ clineAllowSpawnAgent;
523
+ clineAllowAgentTeams;
509
524
  constructor(_oauthManager, options = {}, _clientOptions, executorOptions) {
510
525
  // Initialize tools config (hybrid Gateway/MCP routing)
511
526
  this.toolsConfig = {
@@ -525,7 +540,9 @@ class AgentLoop {
525
540
  const useGatewayMode = gatewayTools.includes('*') || gatewayTools.length > 0;
526
541
  const useMCPMode = mcpTools.includes('*') || mcpTools.length > 0;
527
542
  this.useCodeAct = options.useCodeAct ?? false;
528
- this.isGatewayMode = useGatewayMode || (options.backend === 'codex' && this.useCodeAct);
543
+ this.isGatewayMode =
544
+ useGatewayMode ||
545
+ ((options.backend === 'codex' || options.backend === 'cline') && this.useCodeAct);
529
546
  this.disallowedTools = this.useCodeAct
530
547
  ? options.disallowedTools
531
548
  : [
@@ -535,6 +552,13 @@ class AgentLoop {
535
552
  CODE_ACT_MCP_COMPAT_NAME,
536
553
  ]),
537
554
  ];
555
+ this.clineNativeAllowedTools = [...(options.clineNativeAllowedTools ?? [])];
556
+ this.clineNativeDisallowedTools = [...(options.clineNativeDisallowedTools ?? [])];
557
+ this.clineNativePolicyConfigured =
558
+ options.clineNativeAllowedTools !== undefined ||
559
+ options.clineNativeDisallowedTools !== undefined;
560
+ this.clineAllowSpawnAgent = options.clineAllowSpawnAgent ?? false;
561
+ this.clineAllowAgentTeams = options.clineAllowAgentTeams ?? false;
538
562
  if (useGatewayMode && useMCPMode) {
539
563
  logger.debug('🔀 Hybrid mode: Gateway + MCP tools enabled');
540
564
  }
@@ -691,6 +715,25 @@ class AgentLoop {
691
715
  });
692
716
  logger.debug('Codex app-server backend enabled');
693
717
  }
718
+ else if (this.backend === 'cline') {
719
+ const workspaceDir = options.clineCwd ?? (0, path_1.join)((0, os_1.homedir)(), '.mama', 'workspace');
720
+ if (!(0, fs_1.existsSync)(workspaceDir)) {
721
+ (0, fs_1.mkdirSync)(workspaceDir, { recursive: true });
722
+ }
723
+ this.agent = new cline_cli_adapter_js_1.ClineCLIAdapter({
724
+ command: options.clineCommand,
725
+ provider: options.clineProvider ?? 'cline',
726
+ model: options.model,
727
+ systemPrompt: defaultSystemPrompt,
728
+ cwd: workspaceDir,
729
+ // Cline receives MAMA CodeAct as a session-bound Hub custom tool.
730
+ // Its shared daemon owns native MCP settings independently.
731
+ dataDir: options.clineDataDir,
732
+ requestTimeout: options.timeoutMs,
733
+ sessionId,
734
+ });
735
+ logger.debug('Cline CLI backend enabled');
736
+ }
694
737
  else {
695
738
  // Claude backend: always use PersistentCLI for fast responses (~2-3s vs ~16-30s)
696
739
  this.persistentCLI = new persistent_cli_adapter_js_1.PersistentCLIAdapter({
@@ -981,8 +1024,20 @@ class AgentLoop {
981
1024
  let ownedSession = false;
982
1025
  // Set session ID on the agent
983
1026
  // Claude PersistentCLI: process alive → CONTINUE (stdin message), process dead → NEW (spawn with --session-id)
984
- // Codex: threadId alive → CONTINUE (codex-reply), threadId null → NEW (codex tool)
1027
+ // Codex/Cline: durable runtime session alive → CONTINUE, missing → NEW.
985
1028
  const isCodex = this.backend === 'codex';
1029
+ const isCline = this.backend === 'cline';
1030
+ const isDurableRuntime = isCodex || isCline;
1031
+ const clineRole = options?.agentContext?.role;
1032
+ const clineAllowedTools = this.clineNativePolicyConfigured
1033
+ ? this.clineNativeAllowedTools
1034
+ : (0, cline_native_tool_policy_js_1.projectClineNativeTools)(clineRole?.allowedTools);
1035
+ const clineDisallowedTools = this.clineNativePolicyConfigured
1036
+ ? this.clineNativeDisallowedTools
1037
+ : (0, cline_native_tool_policy_js_1.projectClineNativeTools)(clineRole?.blockedTools);
1038
+ const clineDelegationBlocked = clineRole?.blockedTools?.includes('delegate') ?? false;
1039
+ const clineAllowSpawnAgent = this.clineAllowSpawnAgent && !clineDelegationBlocked;
1040
+ const clineAllowAgentTeams = this.clineAllowAgentTeams && !clineDelegationBlocked;
986
1041
  const codeActPolicy = this.useCodeAct
987
1042
  ? (0, index_js_1.projectCodeActToolPolicy)({
988
1043
  tier: runScope.tier,
@@ -995,12 +1050,12 @@ class AgentLoop {
995
1050
  })
996
1051
  : undefined;
997
1052
  const outerCodeActAllowed = this.useCodeAct && roleAllowsOuterCodeAct(options?.agentContext?.role, this.disallowedTools);
998
- const effectiveSessionPolicyFingerprint = isCodex && codeActPolicy
1053
+ const effectiveSessionPolicyFingerprint = isDurableRuntime && codeActPolicy
999
1054
  ? combineCodeActSessionPolicyFingerprint(options?.sessionPolicyFingerprint, codeActPolicy)
1000
1055
  : options?.sessionPolicyFingerprint;
1001
1056
  let resolvedCliSessionId = options?.cliSessionId ?? null;
1002
1057
  const sessionLabel = (isNew) => {
1003
- if (isCodex) {
1058
+ if (isDurableRuntime) {
1004
1059
  return isNew ? 'NEW thread' : 'CONTINUE thread';
1005
1060
  }
1006
1061
  return isNew ? 'NEW process' : 'CONTINUE session';
@@ -1009,7 +1064,7 @@ class AgentLoop {
1009
1064
  // Session routing travels per prompt() call via resolvedCliSessionId - no
1010
1065
  // shared-adapter mutation (setSessionId re-pointed channelKey/currentProcess
1011
1066
  // across awaits, cross-wiring concurrent lanes).
1012
- console.log(`[AgentLoop] [${isCodex ? 'codex' : 'claude'}] ${channelKey} (${sessionLabel(sessionIsNew)})`);
1067
+ console.log(`[AgentLoop] [${this.backend}] ${channelKey} (${sessionLabel(sessionIsNew)})`);
1013
1068
  }
1014
1069
  else if (options?.freshSession) {
1015
1070
  // Stateless lanes (operator reports): session context is a cache, not
@@ -1020,7 +1075,7 @@ class AgentLoop {
1020
1075
  sessionIsNew = true;
1021
1076
  ownedSession = true;
1022
1077
  resolvedCliSessionId = cliSessionId;
1023
- console.log(`[AgentLoop] [${isCodex ? 'codex' : 'claude'}] ${channelKey} (FRESH session - stateless lane)`);
1078
+ console.log(`[AgentLoop] [${this.backend}] ${channelKey} (FRESH session - stateless lane)`);
1024
1079
  }
1025
1080
  else {
1026
1081
  // Fallback: get session from pool (for direct AgentLoop usage)
@@ -1033,7 +1088,7 @@ class AgentLoop {
1033
1088
  ownedSession = true;
1034
1089
  resolvedCliSessionId = cliSessionId;
1035
1090
  // Per-call routing via resolvedCliSessionId - no shared-adapter mutation.
1036
- console.log(`[AgentLoop] [${isCodex ? 'codex' : 'claude'}] ${channelKey} (${sessionLabel(isNew)})`);
1091
+ console.log(`[AgentLoop] [${this.backend}] ${channelKey} (${sessionLabel(isNew)})`);
1037
1092
  }
1038
1093
  try {
1039
1094
  if (this.shouldBeginModelRun(options)) {
@@ -1047,7 +1102,7 @@ class AgentLoop {
1047
1102
  let nativeToolCallCount = 0;
1048
1103
  let nativeConsecutiveToolCalls = 0;
1049
1104
  let nativeLastToolSignature = '';
1050
- const hostToolBridge = isCodex && this.isGatewayMode
1105
+ const hostToolBridge = isDurableRuntime && this.isGatewayMode
1051
1106
  ? {
1052
1107
  tools: this.useCodeAct
1053
1108
  ? outerCodeActAllowed
@@ -1089,13 +1144,18 @@ class AgentLoop {
1089
1144
  name: call.name,
1090
1145
  input: call.input,
1091
1146
  };
1092
- history.push({ role: 'assistant', content: [toolUse] });
1093
- runScope.onTurn?.({
1094
- turn,
1095
- role: 'assistant',
1096
- content: [toolUse],
1097
- stopReason: 'tool_use',
1098
- });
1147
+ // Codex does not return completed host exchanges, so record them
1148
+ // here. Cline reports the paired custom-tool exchange from its Hub
1149
+ // event stream and AgentLoop appends it exactly once below.
1150
+ if (isCodex) {
1151
+ history.push({ role: 'assistant', content: [toolUse] });
1152
+ runScope.onTurn?.({
1153
+ turn,
1154
+ role: 'assistant',
1155
+ content: [toolUse],
1156
+ stopReason: 'tool_use',
1157
+ });
1158
+ }
1099
1159
  const callExecutionContext = toolExecutionContext
1100
1160
  ? {
1101
1161
  ...toolExecutionContext,
@@ -1115,13 +1175,15 @@ class AgentLoop {
1115
1175
  if (!toolResult.terminalCode) {
1116
1176
  callSignal.throwIfAborted();
1117
1177
  }
1118
- const persistedToolResult = historyToolResult(toolResult);
1119
- history.push({ role: 'user', content: [persistedToolResult] });
1120
- runScope.onTurn?.({
1121
- turn,
1122
- role: 'user',
1123
- content: [persistedToolResult],
1124
- });
1178
+ if (isCodex) {
1179
+ const persistedToolResult = historyToolResult(toolResult);
1180
+ history.push({ role: 'user', content: [persistedToolResult] });
1181
+ runScope.onTurn?.({
1182
+ turn,
1183
+ role: 'user',
1184
+ content: [persistedToolResult],
1185
+ });
1186
+ }
1125
1187
  return {
1126
1188
  content: toolResult.content,
1127
1189
  isError: toolResult.is_error === true,
@@ -1141,7 +1203,7 @@ class AgentLoop {
1141
1203
  if (outerCodeActAllowed) {
1142
1204
  const policy = codeActPolicy;
1143
1205
  const typeDefs = index_js_1.TypeDefinitionGenerator.generate(policy);
1144
- gatewayToolsPrompt = wrapGeneratedPromptSection('codeAct', (0, index_js_1.getCodeActInstructions)(isCodex ? 'codex' : 'claude', policy.names) +
1206
+ gatewayToolsPrompt = wrapGeneratedPromptSection('codeAct', (0, index_js_1.getCodeActInstructions)(this.backend, policy.names) +
1145
1207
  '\n```typescript\n' +
1146
1208
  typeDefs +
1147
1209
  '\n```');
@@ -1201,7 +1263,13 @@ class AgentLoop {
1201
1263
  return effectivePrompt;
1202
1264
  };
1203
1265
  let perCallSystemPrompt;
1204
- if (options?.systemPrompt ||
1266
+ if (isCline && this.isGatewayMode && this.useCodeAct && !sessionIsNew) {
1267
+ // TG-05: a compatible Cline Hub session ignores the per-call system prompt.
1268
+ // Keep the continuation stub cheap; missing/mismatched sessions rebuild the
1269
+ // complete policy lazily through freshSessionSystemPrompt below.
1270
+ perCallSystemPrompt = options?.systemPrompt ?? this.defaultSystemPrompt;
1271
+ }
1272
+ else if (options?.systemPrompt ||
1205
1273
  options?.gatewayToolsPrompt !== undefined ||
1206
1274
  (this.isGatewayMode && this.useCodeAct)) {
1207
1275
  perCallSystemPrompt = prepareSystemPrompt(options?.systemPrompt, options?.resumeSession === true);
@@ -1210,15 +1278,15 @@ class AgentLoop {
1210
1278
  perCallSystemPrompt = this.defaultSystemPrompt;
1211
1279
  console.log(`[AgentLoop] No systemPrompt in options - using spawn default for this call`);
1212
1280
  }
1213
- // Codex re-anchors a rehydrated durable thread through thread/resume's
1214
- // baseInstructions rather than replaying instructions as user text. A CONTINUE
1281
+ // Durable backends re-anchor a lost session with the complete current policy.
1282
+ // A CONTINUE
1215
1283
  // turn's per-call prompt is deliberately minimal (message-router
1216
1284
  // buildMinimalResumePrompt), so resuming with it would REPLACE the thread's full
1217
1285
  // policy with a stub - only freshSessionSystemPrompt rebuilds the complete one.
1218
1286
  // That rebuild costs an embedding search, so hand the backend a lazy builder it
1219
1287
  // invokes only inside the resume branch; a live thread never pays for it.
1220
1288
  const freshSystemPromptBuilder = options?.freshSessionSystemPrompt;
1221
- const resumeInstructions = isCodex && freshSystemPromptBuilder
1289
+ const resumeInstructions = isDurableRuntime && freshSystemPromptBuilder
1222
1290
  ? async () => prepareSystemPrompt(await freshSystemPromptBuilder(), false)
1223
1291
  : undefined;
1224
1292
  // Reset StopContinuation state for this channel to prevent leaking
@@ -1276,24 +1344,27 @@ class AgentLoop {
1276
1344
  };
1277
1345
  let piResult;
1278
1346
  // Claude: First turn → --session-id (inject system prompt), subsequent → --resume
1279
- // Codex: resumeSession only controls threadId reset (false=new thread, true=continue)
1280
- let shouldResume = isCodex
1347
+ // Codex/Cline: resumeSession controls durable session reset/continuation.
1348
+ let shouldResume = isDurableRuntime
1281
1349
  ? turn > 1 || (options?.freshSession === true ? false : (options?.resumeSession ?? true))
1282
1350
  : !sessionIsNew || turn > 1;
1283
1351
  let requestSystemPrompt = perCallSystemPrompt;
1284
- let provisionalCodexSessionId;
1285
- // Both Claude PersistentCLI and Codex app-server preserve context - only send new messages
1352
+ let provisionalDurableSessionId;
1353
+ // All three backends preserve context and receive only the new user message.
1286
1354
  const promptText = this.formatLastMessageOnly(history);
1287
1355
  const promptStart = Date.now();
1288
1356
  const throwFinalCliError = (error) => {
1289
1357
  const normalizedError = error instanceof Error ? error : new Error(String(error));
1358
+ const modelRunnerErrorCode = normalizedError instanceof model_runner_js_1.ModelRunnerError
1359
+ ? MODEL_RUNNER_AGENT_ERROR_CODES[normalizedError.code]
1360
+ : undefined;
1290
1361
  const errorType = normalizedError instanceof types_js_1.McpResultMissingError ||
1291
1362
  normalizedError instanceof types_js_1.McpCompletedMutationInterruptedError ||
1292
1363
  normalizedError instanceof types_js_1.ClaudeToolStreamProtocolError
1293
1364
  ? normalizedError.code
1294
1365
  : normalizedError instanceof model_runner_js_1.HostToolTerminalError
1295
1366
  ? normalizedError.terminalCode
1296
- : 'CLI_ERROR';
1367
+ : (modelRunnerErrorCode ?? 'CLI_ERROR');
1297
1368
  this.onMetric?.('prompt_error', 1, {
1298
1369
  backend: this.backend,
1299
1370
  error_type: errorType,
@@ -1307,12 +1378,15 @@ class AgentLoop {
1307
1378
  if (normalizedError instanceof model_runner_js_1.HostToolTerminalError) {
1308
1379
  throw new types_js_1.AgentError(normalizedError.message, normalizedError.terminalCode, normalizedError, false);
1309
1380
  }
1381
+ if (normalizedError instanceof model_runner_js_1.HostToolAbortError) {
1382
+ throw new types_js_1.AgentError(normalizedError.message, 'CLI_ERROR', normalizedError, false);
1383
+ }
1310
1384
  if (normalizedError instanceof types_js_1.McpResultMissingError ||
1311
1385
  normalizedError instanceof types_js_1.McpCompletedMutationInterruptedError ||
1312
1386
  normalizedError instanceof types_js_1.ClaudeToolStreamProtocolError) {
1313
1387
  throw new types_js_1.AgentError(normalizedError.message, normalizedError.code, normalizedError, false);
1314
1388
  }
1315
- throw new types_js_1.AgentError(`CLI error: ${normalizedError.message}`, 'CLI_ERROR', normalizedError, true);
1389
+ throw new types_js_1.AgentError(`CLI error: ${normalizedError.message}`, modelRunnerErrorCode ?? 'CLI_ERROR', normalizedError, normalizedError instanceof model_runner_js_1.ModelRunnerError ? normalizedError.retryable : true);
1316
1390
  };
1317
1391
  const appendCompletedToolExchanges = (exchanges) => {
1318
1392
  for (const exchange of exchanges) {
@@ -1332,7 +1406,7 @@ class AgentLoop {
1332
1406
  }
1333
1407
  };
1334
1408
  try {
1335
- const durablePolicyStatus = isCodex && turn === 1 && shouldResume
1409
+ const durablePolicyStatus = isDurableRuntime && turn === 1 && shouldResume
1336
1410
  ? this.agent.getSessionPolicyStatus?.({
1337
1411
  model: options?.model,
1338
1412
  resumeSession: true,
@@ -1342,15 +1416,23 @@ class AgentLoop {
1342
1416
  sessionId: resolvedCliSessionId ?? undefined,
1343
1417
  requestTimeout: options?.requestTimeoutMs,
1344
1418
  hostToolBridge,
1419
+ ...(isCline
1420
+ ? {
1421
+ allowedTools: clineAllowedTools,
1422
+ disallowedTools: clineDisallowedTools,
1423
+ allowSpawnAgent: clineAllowSpawnAgent,
1424
+ allowAgentTeams: clineAllowAgentTeams,
1425
+ }
1426
+ : {}),
1345
1427
  })
1346
1428
  : undefined;
1347
1429
  if (durablePolicyStatus === 'mismatch' || durablePolicyStatus === 'missing') {
1348
- console.log(`[AgentLoop] Codex durable session ${durablePolicyStatus}; ` +
1430
+ console.log(`[AgentLoop] ${this.backend} durable session ${durablePolicyStatus}; ` +
1349
1431
  'opening the full policy before model request');
1350
1432
  const newSessionId = this.sessionPool.resetSession(channelKey);
1351
1433
  options?.onCliSessionReset?.(newSessionId);
1352
1434
  resolvedCliSessionId = newSessionId;
1353
- provisionalCodexSessionId = newSessionId;
1435
+ provisionalDurableSessionId = newSessionId;
1354
1436
  try {
1355
1437
  if (options?.freshSessionSystemPrompt) {
1356
1438
  requestSystemPrompt = prepareSystemPrompt(await options.freshSessionSystemPrompt(), false);
@@ -1358,7 +1440,7 @@ class AgentLoop {
1358
1440
  }
1359
1441
  catch (rebuildError) {
1360
1442
  this.sessionPool.invalidateSession(channelKey, newSessionId);
1361
- provisionalCodexSessionId = undefined;
1443
+ provisionalDurableSessionId = undefined;
1362
1444
  attemptReportedError =
1363
1445
  rebuildError instanceof Error ? rebuildError : new Error(String(rebuildError));
1364
1446
  throw rebuildError;
@@ -1378,13 +1460,21 @@ class AgentLoop {
1378
1460
  requestTimeout: options?.requestTimeoutMs,
1379
1461
  hostToolBridge,
1380
1462
  toolExecutionContext,
1463
+ ...(isCline
1464
+ ? {
1465
+ allowedTools: clineAllowedTools,
1466
+ disallowedTools: clineDisallowedTools,
1467
+ allowSpawnAgent: clineAllowSpawnAgent,
1468
+ allowAgentTeams: clineAllowAgentTeams,
1469
+ }
1470
+ : {}),
1381
1471
  });
1382
- provisionalCodexSessionId = undefined;
1472
+ provisionalDurableSessionId = undefined;
1383
1473
  }
1384
1474
  catch (error) {
1385
- if (provisionalCodexSessionId) {
1386
- this.sessionPool.invalidateSession(channelKey, provisionalCodexSessionId);
1387
- provisionalCodexSessionId = undefined;
1475
+ if (provisionalDurableSessionId) {
1476
+ this.sessionPool.invalidateSession(channelKey, provisionalDurableSessionId);
1477
+ provisionalDurableSessionId = undefined;
1388
1478
  }
1389
1479
  const errorMessage = error instanceof Error ? error.message : String(error);
1390
1480
  console.error(`[AgentLoop] ${this.backend} CLI error:`, errorMessage);
@@ -1396,13 +1486,18 @@ class AgentLoop {
1396
1486
  appendCompletedToolExchanges(error.completedToolExchanges);
1397
1487
  throwFinalCliError(error);
1398
1488
  }
1489
+ if (error instanceof model_runner_js_1.HostToolAbortError && error.completedToolExchanges.length > 0) {
1490
+ appendCompletedToolExchanges(error.completedToolExchanges);
1491
+ throwFinalCliError(error);
1492
+ }
1399
1493
  // Check if this is a recoverable session error
1400
1494
  // 1. "No conversation found" - CLI session was lost (daemon restart, timeout)
1401
1495
  // 2. "Session ID already in use" - concurrent request conflict
1402
1496
  // 3. "Prompt is too long" - session context exceeded API limits
1403
1497
  const isSessionNotFound = errorMessage.includes('No conversation found with session ID');
1404
1498
  const isSessionInUse = errorMessage.includes('is already in use');
1405
- const isPromptTooLong = errorMessage.includes('Prompt is too long') ||
1499
+ const isPromptTooLong = (error instanceof model_runner_js_1.ModelRunnerError && error.code === 'context_overflow') ||
1500
+ errorMessage.includes('Prompt is too long') ||
1406
1501
  errorMessage.includes('prompt is too long') ||
1407
1502
  errorMessage.includes('request_too_large') ||
1408
1503
  errorMessage.includes('context window') ||
@@ -1433,16 +1528,16 @@ class AgentLoop {
1433
1528
  // Per-call routing: hand the new id to this prompt() and update the
1434
1529
  // resolved id so later turns follow it - no shared-adapter mutation.
1435
1530
  resolvedCliSessionId = newSessionId;
1436
- // A policy mismatch can occur on a resumed MessageRouter session,
1437
- // whose per-call prompt is intentionally minimal. Rebuild the full
1438
- // policy prompt before opening the replacement durable thread.
1531
+ // A recoverable durable-session error can occur on a resumed
1532
+ // MessageRouter session whose per-call prompt is intentionally minimal.
1533
+ // Rebuild the full policy before opening every replacement session.
1439
1534
  let resetSystemPrompt = perCallSystemPrompt;
1440
1535
  try {
1441
1536
  // Discard the recoverable first-attempt error before any reset
1442
1537
  // preparation. A prompt rebuild or retry failure must surface
1443
1538
  // its own final error, never the mismatch that triggered it.
1444
1539
  attemptReportedError = undefined;
1445
- if (isCodexPolicyMismatch && options?.freshSessionSystemPrompt) {
1540
+ if (options?.freshSessionSystemPrompt) {
1446
1541
  resetSystemPrompt = prepareSystemPrompt(await options.freshSessionSystemPrompt(), false);
1447
1542
  }
1448
1543
  piResult = await this.agent.prompt(promptText, callbacks, {
@@ -1456,6 +1551,14 @@ class AgentLoop {
1456
1551
  requestTimeout: options?.requestTimeoutMs,
1457
1552
  hostToolBridge,
1458
1553
  toolExecutionContext,
1554
+ ...(isCline
1555
+ ? {
1556
+ allowedTools: clineAllowedTools,
1557
+ disallowedTools: clineDisallowedTools,
1558
+ allowSpawnAgent: clineAllowSpawnAgent,
1559
+ allowAgentTeams: clineAllowAgentTeams,
1560
+ }
1561
+ : {}),
1459
1562
  });
1460
1563
  }
1461
1564
  catch (retryError) {
@@ -1493,7 +1596,7 @@ class AgentLoop {
1493
1596
  const contentBlocks = [];
1494
1597
  let parsedToolCalls = [];
1495
1598
  // Parse tool_call / code_act blocks from text response (Gateway Tools mode ONLY)
1496
- if (this.isGatewayMode && !isCodex) {
1599
+ if (this.isGatewayMode && !isDurableRuntime) {
1497
1600
  parsedToolCalls = this.parseToolCallsFromText(piResult.response || '');
1498
1601
  // Code-Act: parse ```js blocks only if enabled
1499
1602
  if (this.useCodeAct) {
@@ -1570,9 +1673,9 @@ class AgentLoop {
1570
1673
  // Ignore recording errors - never break the agent loop
1571
1674
  }
1572
1675
  }
1573
- // Track tokens in session pool for auto-reset at 80% context
1574
- const tokenBackend = this.backend === 'codex' ? 'codex' : 'claude';
1575
- const tokenStatus = this.sessionPool.updateTokens(channelKey, response.usage.input_tokens, tokenBackend);
1676
+ // Preserve the SessionPool usage-status contract. Billing telemetry is
1677
+ // recorded above; durable runtimes own model-aware compaction.
1678
+ const tokenStatus = this.sessionPool.updateTokens(channelKey, response.usage.input_tokens, this.backend);
1576
1679
  // Claude's MCP server may already have executed these tool calls while the
1577
1680
  // prompt was streaming. Preserve the observed exchange in conversation
1578
1681
  // order, but never put the completed tool_use back into executeTools().
@@ -1588,7 +1691,8 @@ class AgentLoop {
1588
1691
  if (piResult.terminalError) {
1589
1692
  throwFinalCliError(new model_runner_js_1.HostToolTerminalError(piResult.terminalError.code, piResult.terminalError.message));
1590
1693
  }
1591
- // PreCompact: inject compaction summary when approaching context limit
1694
+ // PreCompact remains available for a future authoritative occupancy
1695
+ // signal. Runtime-owned sessions do not infer one from usage telemetry.
1592
1696
  if (tokenStatus.nearThreshold && this.preCompactHandler && !runScope.preCompactInjected) {
1593
1697
  runScope.preCompactInjected = true;
1594
1698
  try {
@@ -2016,6 +2120,7 @@ class AgentLoop {
2016
2120
  * Persistent CLI maintains context automatically, so we only send the new message
2017
2121
  */
2018
2122
  formatLastMessageOnly(history) {
2123
+ const imageReaderTool = this.backend === 'cline' ? 'read_files' : this.backend === 'codex' ? 'view_image' : 'Read';
2019
2124
  // Find the last user message in the history
2020
2125
  for (let i = history.length - 1; i >= 0; i--) {
2021
2126
  const msg = history[i];
@@ -2035,9 +2140,9 @@ class AgentLoop {
2035
2140
  else if (block.type === 'image' && block.localPath) {
2036
2141
  parts.push(`⚠️ CRITICAL: The user has uploaded an image file.\n` +
2037
2142
  `Image path: ${block.localPath}\n` +
2038
- `You MUST call the Read tool on "${block.localPath}" to view this image FIRST.\n` +
2143
+ `You MUST call the ${imageReaderTool} tool on "${block.localPath}" to view this image FIRST.\n` +
2039
2144
  `DO NOT describe or guess the image contents without reading it.\n` +
2040
- `DO NOT say you cannot read images - the Read tool supports image files.`);
2145
+ `DO NOT say you cannot read images - ${imageReaderTool} supports image files.`);
2041
2146
  }
2042
2147
  else if (block.type === 'image' && block.source?.data) {
2043
2148
  // Base64-encoded image — save to disk so persistent CLI can read it
@@ -2061,9 +2166,9 @@ class AgentLoop {
2061
2166
  fs.writeFileSync(imagePath, Buffer.from(block.source.data, 'base64'));
2062
2167
  parts.push(`⚠️ CRITICAL: The user has uploaded an image file.\n` +
2063
2168
  `Image path: ${imagePath}\n` +
2064
- `You MUST call the Read tool on "${imagePath}" to view this image FIRST.\n` +
2169
+ `You MUST call the ${imageReaderTool} tool on "${imagePath}" to view this image FIRST.\n` +
2065
2170
  `DO NOT describe or guess the image contents without reading it.\n` +
2066
- `DO NOT say you cannot read images - the Read tool supports image files.`);
2171
+ `DO NOT say you cannot read images - ${imageReaderTool} supports image files.`);
2067
2172
  }
2068
2173
  catch {
2069
2174
  parts.push('[Image attached but could not be processed]');