@jungjaehoon/mama-os 0.23.0 → 0.24.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 (190) hide show
  1. package/dist/agent/agent-loop.d.ts +3 -6
  2. package/dist/agent/agent-loop.d.ts.map +1 -1
  3. package/dist/agent/agent-loop.js +390 -202
  4. package/dist/agent/agent-loop.js.map +1 -1
  5. package/dist/agent/code-act/constants.d.ts +2 -2
  6. package/dist/agent/code-act/constants.d.ts.map +1 -1
  7. package/dist/agent/code-act/constants.js +16 -9
  8. package/dist/agent/code-act/constants.js.map +1 -1
  9. package/dist/agent/code-act/host-bridge.d.ts +12 -11
  10. package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
  11. package/dist/agent/code-act/host-bridge.js +92 -15
  12. package/dist/agent/code-act/host-bridge.js.map +1 -1
  13. package/dist/agent/code-act/index.d.ts +2 -0
  14. package/dist/agent/code-act/index.d.ts.map +1 -1
  15. package/dist/agent/code-act/index.js +5 -1
  16. package/dist/agent/code-act/index.js.map +1 -1
  17. package/dist/agent/code-act/tool-policy.d.ts +42 -0
  18. package/dist/agent/code-act/tool-policy.d.ts.map +1 -0
  19. package/dist/agent/code-act/tool-policy.js +122 -0
  20. package/dist/agent/code-act/tool-policy.js.map +1 -0
  21. package/dist/agent/code-act/type-definition-generator.d.ts +4 -3
  22. package/dist/agent/code-act/type-definition-generator.d.ts.map +1 -1
  23. package/dist/agent/code-act/type-definition-generator.js +26 -48
  24. package/dist/agent/code-act/type-definition-generator.js.map +1 -1
  25. package/dist/agent/codex-app-server-process.d.ts +116 -0
  26. package/dist/agent/codex-app-server-process.d.ts.map +1 -0
  27. package/dist/agent/codex-app-server-process.js +1382 -0
  28. package/dist/agent/codex-app-server-process.js.map +1 -0
  29. package/dist/agent/codex-home.d.ts +9 -0
  30. package/dist/agent/codex-home.d.ts.map +1 -1
  31. package/dist/agent/codex-home.js +626 -4
  32. package/dist/agent/codex-home.js.map +1 -1
  33. package/dist/agent/codex-thread-registry.d.ts +63 -0
  34. package/dist/agent/codex-thread-registry.d.ts.map +1 -0
  35. package/dist/agent/codex-thread-registry.js +433 -0
  36. package/dist/agent/codex-thread-registry.js.map +1 -0
  37. package/dist/agent/gateway-tool-executor.d.ts +2 -7
  38. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  39. package/dist/agent/gateway-tool-executor.js +69 -84
  40. package/dist/agent/gateway-tool-executor.js.map +1 -1
  41. package/dist/agent/managed-agent-runtime-sync.d.ts.map +1 -1
  42. package/dist/agent/managed-agent-runtime-sync.js +0 -3
  43. package/dist/agent/managed-agent-runtime-sync.js.map +1 -1
  44. package/dist/agent/managed-agent-validation.js +1 -1
  45. package/dist/agent/managed-agent-validation.js.map +1 -1
  46. package/dist/agent/model-runner.d.ts +51 -3
  47. package/dist/agent/model-runner.d.ts.map +1 -1
  48. package/dist/agent/model-runner.js +1 -1
  49. package/dist/agent/model-runner.js.map +1 -1
  50. package/dist/agent/persistent-cli-adapter.d.ts +1 -0
  51. package/dist/agent/persistent-cli-adapter.d.ts.map +1 -1
  52. package/dist/agent/persistent-cli-adapter.js +3 -0
  53. package/dist/agent/persistent-cli-adapter.js.map +1 -1
  54. package/dist/agent/session-pool.d.ts +2 -2
  55. package/dist/agent/session-pool.d.ts.map +1 -1
  56. package/dist/agent/session-pool.js +6 -6
  57. package/dist/agent/session-pool.js.map +1 -1
  58. package/dist/agent/tool-registry.d.ts +14 -0
  59. package/dist/agent/tool-registry.d.ts.map +1 -1
  60. package/dist/agent/tool-registry.js +46 -7
  61. package/dist/agent/tool-registry.js.map +1 -1
  62. package/dist/agent/types.d.ts +40 -6
  63. package/dist/agent/types.d.ts.map +1 -1
  64. package/dist/agent/types.js.map +1 -1
  65. package/dist/api/graph-api.d.ts.map +1 -1
  66. package/dist/api/graph-api.js +19 -7
  67. package/dist/api/graph-api.js.map +1 -1
  68. package/dist/cli/commands/init.d.ts +1 -1
  69. package/dist/cli/commands/init.d.ts.map +1 -1
  70. package/dist/cli/commands/init.js +22 -14
  71. package/dist/cli/commands/init.js.map +1 -1
  72. package/dist/cli/commands/run.d.ts.map +1 -1
  73. package/dist/cli/commands/run.js +7 -4
  74. package/dist/cli/commands/run.js.map +1 -1
  75. package/dist/cli/commands/start.d.ts +2 -0
  76. package/dist/cli/commands/start.d.ts.map +1 -1
  77. package/dist/cli/commands/start.js +77 -24
  78. package/dist/cli/commands/start.js.map +1 -1
  79. package/dist/cli/commands/status.js +2 -2
  80. package/dist/cli/commands/status.js.map +1 -1
  81. package/dist/cli/config/config-manager.d.ts.map +1 -1
  82. package/dist/cli/config/config-manager.js +21 -4
  83. package/dist/cli/config/config-manager.js.map +1 -1
  84. package/dist/cli/config/types.d.ts +4 -4
  85. package/dist/cli/config/types.d.ts.map +1 -1
  86. package/dist/cli/config/types.js +1 -0
  87. package/dist/cli/config/types.js.map +1 -1
  88. package/dist/cli/index.js +2 -2
  89. package/dist/cli/index.js.map +1 -1
  90. package/dist/cli/runtime/agent-loop-init.d.ts +1 -1
  91. package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
  92. package/dist/cli/runtime/agent-loop-init.js +12 -11
  93. package/dist/cli/runtime/agent-loop-init.js.map +1 -1
  94. package/dist/cli/runtime/connector-init.d.ts.map +1 -1
  95. package/dist/cli/runtime/connector-init.js +13 -17
  96. package/dist/cli/runtime/connector-init.js.map +1 -1
  97. package/dist/cli/runtime/envelope-bootstrap.d.ts.map +1 -1
  98. package/dist/cli/runtime/envelope-bootstrap.js +13 -1
  99. package/dist/cli/runtime/envelope-bootstrap.js.map +1 -1
  100. package/dist/cli/runtime/gateway-init.d.ts +1 -1
  101. package/dist/cli/runtime/gateway-init.d.ts.map +1 -1
  102. package/dist/cli/runtime/gateway-init.js.map +1 -1
  103. package/dist/cli/runtime/mama-core-init.d.ts +2 -2
  104. package/dist/cli/runtime/mama-core-init.d.ts.map +1 -1
  105. package/dist/cli/runtime/mama-core-init.js +17 -0
  106. package/dist/cli/runtime/mama-core-init.js.map +1 -1
  107. package/dist/cli/runtime/memory-agent-init.d.ts +1 -1
  108. package/dist/cli/runtime/memory-agent-init.d.ts.map +1 -1
  109. package/dist/cli/runtime/memory-agent-init.js +16 -6
  110. package/dist/cli/runtime/memory-agent-init.js.map +1 -1
  111. package/dist/cli/runtime/types.d.ts +4 -26
  112. package/dist/cli/runtime/types.d.ts.map +1 -1
  113. package/dist/cli/runtime/utilities.js +2 -2
  114. package/dist/cli/runtime/utilities.js.map +1 -1
  115. package/dist/connectors/config-loader.d.ts +24 -0
  116. package/dist/connectors/config-loader.d.ts.map +1 -0
  117. package/dist/connectors/config-loader.js +205 -0
  118. package/dist/connectors/config-loader.js.map +1 -0
  119. package/dist/connectors/framework/types.d.ts +2 -0
  120. package/dist/connectors/framework/types.d.ts.map +1 -1
  121. package/dist/connectors/kagemusha/query-tools.js +2 -2
  122. package/dist/connectors/kagemusha/query-tools.js.map +1 -1
  123. package/dist/envelope/reactive-config.d.ts +2 -2
  124. package/dist/envelope/reactive-config.d.ts.map +1 -1
  125. package/dist/envelope/reactive-config.js +13 -6
  126. package/dist/envelope/reactive-config.js.map +1 -1
  127. package/dist/gateways/context-injector.d.ts +2 -1
  128. package/dist/gateways/context-injector.d.ts.map +1 -1
  129. package/dist/gateways/context-injector.js.map +1 -1
  130. package/dist/gateways/message-router.d.ts +1 -0
  131. package/dist/gateways/message-router.d.ts.map +1 -1
  132. package/dist/gateways/message-router.js +25 -3
  133. package/dist/gateways/message-router.js.map +1 -1
  134. package/dist/gateways/types.d.ts +1 -1
  135. package/dist/gateways/types.d.ts.map +1 -1
  136. package/dist/multi-agent/agent-process-manager.d.ts +9 -1
  137. package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
  138. package/dist/multi-agent/agent-process-manager.js +197 -49
  139. package/dist/multi-agent/agent-process-manager.js.map +1 -1
  140. package/dist/multi-agent/multi-agent-base.d.ts.map +1 -1
  141. package/dist/multi-agent/multi-agent-base.js +2 -1
  142. package/dist/multi-agent/multi-agent-base.js.map +1 -1
  143. package/dist/multi-agent/runtime-process.d.ts +18 -15
  144. package/dist/multi-agent/runtime-process.d.ts.map +1 -1
  145. package/dist/multi-agent/runtime-process.js +63 -59
  146. package/dist/multi-agent/runtime-process.js.map +1 -1
  147. package/dist/multi-agent/types.d.ts +13 -7
  148. package/dist/multi-agent/types.d.ts.map +1 -1
  149. package/dist/multi-agent/types.js.map +1 -1
  150. package/dist/multi-agent/workflow-engine.js +3 -3
  151. package/dist/multi-agent/workflow-engine.js.map +1 -1
  152. package/dist/multi-agent/workflow-types.d.ts +2 -2
  153. package/dist/multi-agent/workflow-types.d.ts.map +1 -1
  154. package/dist/operator/operator-trigger-loop.d.ts +3 -0
  155. package/dist/operator/operator-trigger-loop.d.ts.map +1 -1
  156. package/dist/operator/operator-trigger-loop.js +1 -0
  157. package/dist/operator/operator-trigger-loop.js.map +1 -1
  158. package/dist/operator/report-run.d.ts +4 -4
  159. package/dist/operator/situation-report.d.ts +3 -0
  160. package/dist/operator/situation-report.d.ts.map +1 -1
  161. package/dist/operator/situation-report.js +43 -26
  162. package/dist/operator/situation-report.js.map +1 -1
  163. package/dist/operator/trigger-author.d.ts +35 -1
  164. package/dist/operator/trigger-author.d.ts.map +1 -1
  165. package/dist/operator/trigger-author.js +73 -10
  166. package/dist/operator/trigger-author.js.map +1 -1
  167. package/dist/operator/worker-run.d.ts +30 -0
  168. package/dist/operator/worker-run.d.ts.map +1 -1
  169. package/dist/operator/worker-run.js +64 -1
  170. package/dist/operator/worker-run.js.map +1 -1
  171. package/dist/operator/workorder-consumer.d.ts +7 -4
  172. package/dist/operator/workorder-consumer.d.ts.map +1 -1
  173. package/dist/operator/workorder-consumer.js +8 -3
  174. package/dist/operator/workorder-consumer.js.map +1 -1
  175. package/dist/setup/setup-prompt.d.ts +1 -1
  176. package/dist/setup/setup-prompt.d.ts.map +1 -1
  177. package/dist/setup/setup-prompt.js +4 -5
  178. package/dist/setup/setup-prompt.js.map +1 -1
  179. package/package.json +1 -1
  180. package/public/viewer/js/modules/agents.js +13 -9
  181. package/public/viewer/js/modules/settings.js +15 -10
  182. package/public/viewer/src/modules/agents.ts +12 -10
  183. package/public/viewer/src/modules/settings.ts +17 -12
  184. package/public/viewer/src/utils/api.ts +2 -2
  185. package/templates/AGENTS.codex.md +2 -23
  186. package/templates/personas/conductor.md +1 -1
  187. package/dist/agent/codex-mcp-process.d.ts +0 -105
  188. package/dist/agent/codex-mcp-process.d.ts.map +0 -1
  189. package/dist/agent/codex-mcp-process.js +0 -748
  190. package/dist/agent/codex-mcp-process.js.map +0 -1
@@ -45,6 +45,7 @@ var __importStar = (this && this.__importStar) || (function () {
45
45
  })();
46
46
  Object.defineProperty(exports, "__esModule", { value: true });
47
47
  exports.AgentLoop = void 0;
48
+ exports.sanitizeLegacyCodexAgentsMd = sanitizeLegacyCodexAgentsMd;
48
49
  exports.loadBackendAgentsMd = loadBackendAgentsMd;
49
50
  exports.loadComposedSystemPrompt = loadComposedSystemPrompt;
50
51
  exports.getGatewayToolsPrompt = getGatewayToolsPrompt;
@@ -92,38 +93,6 @@ const SOURCE_GLOBAL_LANES = {
92
93
  // because per-run state is scoped (RunScope), not instance state.
93
94
  operator: 'operator',
94
95
  };
95
- function matchCodeActToolPattern(pattern, toolName) {
96
- if (pattern === '*') {
97
- return true;
98
- }
99
- if (pattern.endsWith('*')) {
100
- return toolName.startsWith(pattern.slice(0, -1));
101
- }
102
- return pattern === toolName;
103
- }
104
- function resolveAllowedCodeActTools(allowedTools, blockedTools) {
105
- if (!allowedTools) {
106
- if (!blockedTools?.length) {
107
- return undefined;
108
- }
109
- if (blockedTools.includes('*')) {
110
- return [];
111
- }
112
- const registryNames = index_js_1.HostBridge.getToolRegistry().map((meta) => meta.name);
113
- return registryNames.filter((toolName) => !blockedTools.some((pattern) => matchCodeActToolPattern(pattern, toolName)));
114
- }
115
- if (!blockedTools?.length) {
116
- return [...allowedTools];
117
- }
118
- if (blockedTools.includes('*')) {
119
- return [];
120
- }
121
- const registryNames = index_js_1.HostBridge.getToolRegistry().map((meta) => meta.name);
122
- const expandedAllowed = allowedTools.includes('*')
123
- ? registryNames
124
- : registryNames.filter((toolName) => allowedTools.some((pattern) => matchCodeActToolPattern(pattern, toolName)));
125
- return expandedAllowed.filter((toolName) => !blockedTools.some((pattern) => matchCodeActToolPattern(pattern, toolName)));
126
- }
127
96
  /**
128
97
  * Load CLAUDE.md system prompt
129
98
  * Tries multiple paths: project root, ~/.mama, /etc/mama
@@ -163,8 +132,79 @@ function loadSystemPrompt(verbose = false) {
163
132
  */
164
133
  /**
165
134
  * Load backend-specific AGENTS.md from ~/.mama/
166
- * Maps backend to file: 'claude' → AGENTS.claude.md, 'codex-mcp' → AGENTS.codex.md
135
+ * Maps backend to file: 'claude' → AGENTS.claude.md, 'codex' → AGENTS.codex.md
167
136
  */
137
+ function sanitizeLegacyCodexAgentsMd(content) {
138
+ const nativeGuidance = [
139
+ 'MAMA exposes the tools permitted for this run as native host tools. Call those tools directly through the model tool interface.',
140
+ '',
141
+ 'Do not print Markdown tool blocks or JavaScript as a substitute for a tool call. The available native tool set is injected for each run and already reflects the current role and channel permissions.',
142
+ ].join('\n');
143
+ const knownLegacySections = [
144
+ {
145
+ heading: '## Tool protocol',
146
+ nextHeading: '## Behavioural traits to know about yourself',
147
+ signature: 'Gateway tools via `tool_call` JSON blocks:',
148
+ },
149
+ {
150
+ heading: '## Tool Usage',
151
+ nextHeading: '### Skills',
152
+ signature: 'Use gateway tools via `tool_call` JSON blocks.',
153
+ },
154
+ ];
155
+ const knownLegacyLines = [
156
+ '### How to Call Tools',
157
+ '### Available Gateway Tools',
158
+ '### Important',
159
+ '- **mama_search**(query?, type?, limit?) — Search decisions in MAMA memory',
160
+ '- **mama_save**(type, topic?, decision?, reasoning?) — Save decision or checkpoint',
161
+ '- **mama_update**(id, outcome, reason?) — Update decision outcome',
162
+ '- **mama_load_checkpoint**() — Load last checkpoint',
163
+ '- **discord_send**(channel_id, message?) — Send message to Discord channel',
164
+ '- **slack_send**(channel_id, message?) — Send message to Slack channel',
165
+ '- **Read**(path) — Read file',
166
+ '- **Write**(path, content) — Write file',
167
+ '- **Bash**(command) — Execute shell command',
168
+ '- Do NOT use `exec_command` or `apply_patch` — use gateway tools instead',
169
+ '- Tool calls are executed automatically. No need to use curl or Bash for these.',
170
+ "Do NOT use `exec_command` or `apply_patch` — those are Codex defaults but bypass MAMA's gateway. Tool calls are executed automatically; do not wrap in `curl` or `Bash`.",
171
+ '## Available gateway tools',
172
+ '`mama_search`, `mama_save`, `mama_recall`, `mama_update`, `mama_load_checkpoint`, `discord_send`, `slack_send`, `Read`, `Write`, `Bash`. Check the skill `SKILL.md` for skill-provided extras.',
173
+ ];
174
+ const removeExactLine = (section, line) => section
175
+ .split(/(\r?\n)/)
176
+ .filter((part) => part !== line)
177
+ .join('');
178
+ let sanitized = content;
179
+ for (const legacy of knownLegacySections) {
180
+ const start = sanitized.indexOf(legacy.heading);
181
+ if (start === -1) {
182
+ continue;
183
+ }
184
+ const end = sanitized.indexOf(legacy.nextHeading, start + legacy.heading.length);
185
+ if (end === -1) {
186
+ continue;
187
+ }
188
+ const section = sanitized.slice(start, end);
189
+ if (!section.includes(legacy.signature) || !section.includes('```tool_call')) {
190
+ continue;
191
+ }
192
+ let transformed = section.replace(legacy.signature, nativeGuidance);
193
+ const toolCallExample = [
194
+ '```tool_call',
195
+ '{"name": "tool_name", "input": {"param1": "value1"}}',
196
+ '```',
197
+ ].join('\n');
198
+ transformed = transformed
199
+ .replaceAll(toolCallExample, '')
200
+ .replaceAll(toolCallExample.replaceAll('\n', '\r\n'), '');
201
+ for (const line of knownLegacyLines) {
202
+ transformed = removeExactLine(transformed, line);
203
+ }
204
+ sanitized = `${sanitized.slice(0, start)}${transformed}${sanitized.slice(end)}`;
205
+ }
206
+ return sanitized;
207
+ }
168
208
  function loadBackendAgentsMd(backend, verbose = false) {
169
209
  if (!backend) {
170
210
  return '';
@@ -172,7 +212,6 @@ function loadBackendAgentsMd(backend, verbose = false) {
172
212
  const keyMap = {
173
213
  claude: 'claude',
174
214
  codex: 'codex',
175
- 'codex-mcp': 'codex',
176
215
  };
177
216
  const key = keyMap[backend];
178
217
  if (!key) {
@@ -183,7 +222,8 @@ function loadBackendAgentsMd(backend, verbose = false) {
183
222
  if (verbose) {
184
223
  console.log(`[AgentLoop] Loaded backend AGENTS.md: AGENTS.${key}.md`);
185
224
  }
186
- return (0, fs_1.readFileSync)(filePath, 'utf-8');
225
+ const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
226
+ return key === 'codex' ? sanitizeLegacyCodexAgentsMd(content) : content;
187
227
  }
188
228
  if (verbose) {
189
229
  console.log(`[AgentLoop] Backend AGENTS.md not found: AGENTS.${key}.md`);
@@ -289,6 +329,98 @@ function getGatewayToolsPrompt(disallowed) {
289
329
  }
290
330
  return filtered;
291
331
  }
332
+ const CANONICAL_CODE_ACT_HEADING = '## Code-Act: Gateway Tool Execution via Sandbox';
333
+ const CODE_ACT_MCP_COMPAT_NAME = 'mcp__code-act__code_act';
334
+ const GENERATED_CODE_ACT_START = '<!-- MAMA_GENERATED_CODE_ACT_START -->';
335
+ const GENERATED_CODE_ACT_END = '<!-- MAMA_GENERATED_CODE_ACT_END -->';
336
+ const GENERATED_GATEWAY_TOOLS_START = '<!-- MAMA_GENERATED_GATEWAY_TOOLS_START -->';
337
+ const GENERATED_GATEWAY_TOOLS_END = '<!-- MAMA_GENERATED_GATEWAY_TOOLS_END -->';
338
+ function removePromptSection(systemPrompt, sectionStart, sectionEnd) {
339
+ const prefix = systemPrompt.slice(0, sectionStart);
340
+ const separator = prefix.match(/\r?\n\r?\n---[ \t]*\r?\n\r?\n$/);
341
+ const removalStart = separator?.index ?? sectionStart;
342
+ return `${systemPrompt.slice(0, removalStart)}${systemPrompt.slice(sectionEnd)}`;
343
+ }
344
+ function stripMarkedPromptSection(systemPrompt, startMarker, endMarker) {
345
+ let stripped = systemPrompt;
346
+ let sectionStart = stripped.lastIndexOf(startMarker);
347
+ while (sectionStart >= 0) {
348
+ const markerEnd = stripped.indexOf(endMarker, sectionStart + startMarker.length);
349
+ if (markerEnd < 0) {
350
+ return stripped;
351
+ }
352
+ stripped = removePromptSection(stripped, sectionStart, markerEnd + endMarker.length);
353
+ sectionStart = stripped.lastIndexOf(startMarker);
354
+ }
355
+ return stripped;
356
+ }
357
+ function wrapGeneratedPromptSection(kind, content) {
358
+ const [start, end] = kind === 'codeAct'
359
+ ? [GENERATED_CODE_ACT_START, GENERATED_CODE_ACT_END]
360
+ : [GENERATED_GATEWAY_TOOLS_START, GENERATED_GATEWAY_TOOLS_END];
361
+ return `${start}\n${content}\n${end}`;
362
+ }
363
+ function stripGenericGatewayToolsCatalog(systemPrompt) {
364
+ const withoutMarked = stripMarkedPromptSection(systemPrompt, GENERATED_GATEWAY_TOOLS_START, GENERATED_GATEWAY_TOOLS_END);
365
+ const headingPattern = /^#{1,6}\s+Gateway Tools\s*$/gm;
366
+ let catalogStart = -1;
367
+ let match;
368
+ while ((match = headingPattern.exec(withoutMarked)) !== null) {
369
+ catalogStart = match.index;
370
+ }
371
+ if (catalogStart < 0) {
372
+ return withoutMarked;
373
+ }
374
+ const catalog = withoutMarked.slice(catalogStart);
375
+ if (!catalog.includes('Call tools via JSON block:') || !catalog.includes('```tool_call')) {
376
+ return withoutMarked;
377
+ }
378
+ const generatedCatalog = getGatewayToolsPrompt();
379
+ if (withoutMarked.startsWith(generatedCatalog, catalogStart)) {
380
+ return removePromptSection(withoutMarked, catalogStart, catalogStart + generatedCatalog.length);
381
+ }
382
+ const boundedSeparator = catalog.match(/\r?\n\r?\n---[ \t]*\r?\n\r?\n/);
383
+ const sectionEnd = boundedSeparator?.index ?? catalog.length;
384
+ return removePromptSection(withoutMarked, catalogStart, catalogStart + sectionEnd);
385
+ }
386
+ function stripTrailingCanonicalCodeActSection(systemPrompt) {
387
+ const withoutMarked = stripMarkedPromptSection(systemPrompt, GENERATED_CODE_ACT_START, GENERATED_CODE_ACT_END);
388
+ const sectionStart = withoutMarked.lastIndexOf(CANONICAL_CODE_ACT_HEADING);
389
+ if (sectionStart < 0) {
390
+ return withoutMarked;
391
+ }
392
+ const section = withoutMarked.slice(sectionStart);
393
+ const typeFenceStart = section.indexOf('```typescript');
394
+ if (typeFenceStart >= 0) {
395
+ const typeFenceEnd = section.indexOf('```', typeFenceStart + '```typescript'.length);
396
+ if (typeFenceEnd >= 0) {
397
+ return removePromptSection(withoutMarked, sectionStart, sectionStart + typeFenceEnd + 3);
398
+ }
399
+ }
400
+ const boundedSeparator = section.match(/\r?\n\r?\n---[ \t]*\r?\n\r?\n/);
401
+ const sectionEnd = boundedSeparator?.index ?? section.length;
402
+ return removePromptSection(withoutMarked, sectionStart, sectionStart + sectionEnd);
403
+ }
404
+ function stripDisabledCodeActGuidance(systemPrompt) {
405
+ return stripTrailingCanonicalCodeActSection(systemPrompt).replace(/^- \*\*(?:code_act|mcp__code-act__code_act)\*\*.*$/gm, '');
406
+ }
407
+ function combineCodeActSessionPolicyFingerprint(callerFingerprint, policy) {
408
+ return JSON.stringify({
409
+ version: 1,
410
+ callerFingerprint: callerFingerprint ?? null,
411
+ codeActPolicy: policy.fingerprintPayload,
412
+ });
413
+ }
414
+ function roleAllowsOuterCodeAct(role, disallowedTools) {
415
+ if (!role) {
416
+ return false;
417
+ }
418
+ return tool_registry_js_1.ToolRegistry.getHostToolDefinitions({
419
+ allowedTools: role.allowedTools,
420
+ blockedTools: role.blockedTools,
421
+ disallowedTools: disallowedTools ? [...disallowedTools] : undefined,
422
+ }).some((tool) => tool.name === index_js_1.CODE_ACT_MARKER);
423
+ }
292
424
  function buildAgentToolExecutionContext(options) {
293
425
  if (!options ||
294
426
  (options.agentContext === undefined &&
@@ -363,7 +495,7 @@ class AgentLoop {
363
495
  stopContinuationHandler;
364
496
  preCompactHandler;
365
497
  // Per-run state (stream callbacks, tier) lives in a RunScope threaded through
366
- // runWithContentInternal -> executeTools -> executeCodeAct, NEVER on the
498
+ // runWithContentInternal -> executeTools, NEVER on the
367
499
  // instance: concurrent runs on separate global lanes (operator report/worker
368
500
  // overlapping owner chat) would steal each other's callbacks and leak tiers
369
501
  // if these were instance fields.
@@ -386,9 +518,17 @@ class AgentLoop {
386
518
  // Hybrid mode: Gateway + MCP both enabled
387
519
  const useGatewayMode = gatewayTools.includes('*') || gatewayTools.length > 0;
388
520
  const useMCPMode = mcpTools.includes('*') || mcpTools.length > 0;
389
- this.isGatewayMode = useGatewayMode;
390
521
  this.useCodeAct = options.useCodeAct ?? false;
391
- this.disallowedTools = options.disallowedTools;
522
+ this.isGatewayMode = useGatewayMode || (options.backend === 'codex' && this.useCodeAct);
523
+ this.disallowedTools = this.useCodeAct
524
+ ? options.disallowedTools
525
+ : [
526
+ ...new Set([
527
+ ...(options.disallowedTools ?? []),
528
+ index_js_1.CODE_ACT_MARKER,
529
+ CODE_ACT_MCP_COMPAT_NAME,
530
+ ]),
531
+ ];
392
532
  if (useGatewayMode && useMCPMode) {
393
533
  logger.debug('🔀 Hybrid mode: Gateway + MCP tools enabled');
394
534
  }
@@ -459,32 +599,42 @@ class AgentLoop {
459
599
  ];
460
600
  }
461
601
  const backend = options.backend ?? 'claude';
602
+ if (this.useCodeAct && options.systemPrompt) {
603
+ promptLayers = [
604
+ {
605
+ name: 'custom',
606
+ content: stripTrailingCanonicalCodeActSection(stripGenericGatewayToolsCatalog(options.systemPrompt)),
607
+ priority: 1,
608
+ },
609
+ ];
610
+ }
462
611
  // Load backend-specific AGENTS.md (e.g., AGENTS.claude.md, AGENTS.codex.md)
463
612
  const backendAgentsMd = loadBackendAgentsMd(backend);
464
613
  if (backendAgentsMd) {
465
614
  promptLayers.push({ name: 'backendAgents', content: backendAgentsMd, priority: 2 });
466
615
  }
467
- if (useGatewayMode) {
468
- if (this.useCodeAct) {
616
+ if (this.isGatewayMode) {
617
+ if (this.useCodeAct &&
618
+ roleAllowsOuterCodeAct(options.agentContext?.role, this.disallowedTools)) {
469
619
  // Code-Act mode: replace verbose gateway tools markdown with compact .d.ts
470
- const tierForTypeDefs = options.agentContext?.tier === 1 ||
471
- options.agentContext?.tier === 2 ||
472
- options.agentContext?.tier === 3
473
- ? options.agentContext.tier
474
- : 1;
475
- const allowedGatewayTools = resolveAllowedCodeActTools(options.agentContext?.role.allowedTools, options.agentContext?.role.blockedTools);
476
- const typeDefs = index_js_1.TypeDefinitionGenerator.generate(tierForTypeDefs, allowedGatewayTools);
477
- const codeActBackend = backend === 'codex' || backend === 'codex-mcp' ? 'codex-mcp' : 'claude';
478
- const codeActPrompt = (0, index_js_1.getCodeActInstructions)(codeActBackend, allowedGatewayTools) +
479
- '\n```typescript\n' +
480
- typeDefs +
481
- '\n```';
620
+ const tierForTypeDefs = options.agentContext?.tier ?? 1;
621
+ const policy = (0, index_js_1.projectCodeActToolPolicy)({
622
+ tier: tierForTypeDefs,
623
+ role: options.agentContext?.role,
624
+ disallowedTools: this.disallowedTools,
625
+ });
626
+ const typeDefs = index_js_1.TypeDefinitionGenerator.generate(policy);
627
+ const codeActPrompt = wrapGeneratedPromptSection('codeAct', (0, index_js_1.getCodeActInstructions)(backend, policy.names) + '\n```typescript\n' + typeDefs + '\n```');
482
628
  promptLayers.push({ name: 'codeAct', content: codeActPrompt, priority: 2 });
483
629
  }
484
- else {
630
+ else if (!this.useCodeAct && backend !== 'codex') {
485
631
  const gatewayToolsPrompt = getGatewayToolsPrompt(this.disallowedTools);
486
632
  if (gatewayToolsPrompt) {
487
- promptLayers.push({ name: 'gatewayTools', content: gatewayToolsPrompt, priority: 2 });
633
+ promptLayers.push({
634
+ name: 'gatewayTools',
635
+ content: wrapGeneratedPromptSection('gatewayTools', gatewayToolsPrompt),
636
+ priority: 2,
637
+ });
488
638
  }
489
639
  }
490
640
  }
@@ -508,9 +658,9 @@ class AgentLoop {
508
658
  this.defaultSystemPrompt = defaultSystemPrompt;
509
659
  // Choose backend (default: claude)
510
660
  this.backend = backend;
511
- if (this.backend === 'codex' || this.backend === 'codex-mcp') {
512
- // Codex MCP mode: standard MCP protocol
513
- const workspaceDir = (0, path_1.join)((0, os_1.homedir)(), '.mama', 'workspace');
661
+ if (this.backend === 'codex') {
662
+ // Codex app-server mode
663
+ const workspaceDir = options.codexCwd ?? (0, path_1.join)((0, os_1.homedir)(), '.mama', 'workspace');
514
664
  // Ensure workspace directory exists
515
665
  if (!(0, fs_1.existsSync)(workspaceDir)) {
516
666
  (0, fs_1.mkdirSync)(workspaceDir, { recursive: true });
@@ -518,12 +668,18 @@ class AgentLoop {
518
668
  this.agent = new runtime_process_js_1.CodexRuntimeProcess({
519
669
  model: options.model,
520
670
  cwd: workspaceDir,
521
- sandbox: 'workspace-write',
671
+ sandbox: options.codexSandbox ?? 'workspace-write',
522
672
  systemPrompt: defaultSystemPrompt,
673
+ command: options.codexCommand,
523
674
  requestTimeout: options.timeoutMs,
524
- mcpConfigPath: options.mcpConfigPath ?? (useMCPMode ? mcpConfigPath : undefined),
675
+ codexHome: options.codexHome,
676
+ isolatedHome: options.codexIsolatedHome,
677
+ registryRoot: options.codexRegistryRoot,
678
+ mcpConfigPath: this.useCodeAct
679
+ ? undefined
680
+ : (options.mcpConfigPath ?? (useMCPMode ? mcpConfigPath : undefined)),
525
681
  });
526
- logger.debug('Codex MCP backend enabled');
682
+ logger.debug('Codex app-server backend enabled');
527
683
  }
528
684
  else {
529
685
  // Claude backend: always use PersistentCLI for fast responses (~2-3s vs ~16-30s)
@@ -540,7 +696,7 @@ class AgentLoop {
540
696
  // Gateway tools are processed by GatewayToolExecutor (hybrid with MCP)
541
697
  useGatewayTools: useGatewayMode,
542
698
  // Structurally disallow specific tools (e.g., Bash/Read for restricted agents)
543
- disallowedTools: options.disallowedTools,
699
+ disallowedTools: this.disallowedTools,
544
700
  // Native built-ins cross with text-parsed gateway calls (hallucinated
545
701
  // ToolSearch/Agent, native gathering that bypasses report verification).
546
702
  // Callers opt in per loop; agent-loop-init locks down the main persona.
@@ -705,6 +861,7 @@ class AgentLoop {
705
861
  * Set AgentProcessManager for delegate tool (multi-agent delegation)
706
862
  */
707
863
  setAgentProcessManager(pm) {
864
+ pm.setGatewayToolExecutor(this.mcpExecutor);
708
865
  this.mcpExecutor.setAgentProcessManager(pm);
709
866
  }
710
867
  /**
@@ -808,7 +965,11 @@ class AgentLoop {
808
965
  // Track this run's tier for code-act execution and prompt sizing.
809
966
  if (options?.agentContext) {
810
967
  const rawTier = options.agentContext.tier ?? 1;
811
- runScope.tier = (rawTier === 1 || rawTier === 2 || rawTier === 3 ? rawTier : 1);
968
+ runScope.tier = this.useCodeAct
969
+ ? (0, index_js_1.requireCodeActTier)(rawTier)
970
+ : rawTier === 1 || rawTier === 2 || rawTier === 3
971
+ ? rawTier
972
+ : 1;
812
973
  }
813
974
  // Infinite loop prevention
814
975
  let consecutiveToolCalls = 0;
@@ -825,7 +986,18 @@ class AgentLoop {
825
986
  // Set session ID on the agent
826
987
  // Claude PersistentCLI: process alive → CONTINUE (stdin message), process dead → NEW (spawn with --session-id)
827
988
  // Codex: threadId alive → CONTINUE (codex-reply), threadId null → NEW (codex tool)
828
- const isCodex = this.backend === 'codex' || this.backend === 'codex-mcp';
989
+ const isCodex = this.backend === 'codex';
990
+ const codeActPolicy = this.useCodeAct
991
+ ? (0, index_js_1.projectCodeActToolPolicy)({
992
+ tier: runScope.tier,
993
+ role: options?.agentContext?.role,
994
+ disallowedTools: this.disallowedTools,
995
+ })
996
+ : undefined;
997
+ const outerCodeActAllowed = this.useCodeAct && roleAllowsOuterCodeAct(options?.agentContext?.role, this.disallowedTools);
998
+ const effectiveSessionPolicyFingerprint = isCodex && codeActPolicy
999
+ ? combineCodeActSessionPolicyFingerprint(options?.sessionPolicyFingerprint, codeActPolicy)
1000
+ : options?.sessionPolicyFingerprint;
829
1001
  let resolvedCliSessionId = options?.cliSessionId ?? null;
830
1002
  const sessionLabel = (isNew) => {
831
1003
  if (isCodex) {
@@ -848,12 +1020,6 @@ class AgentLoop {
848
1020
  sessionIsNew = true;
849
1021
  ownedSession = true;
850
1022
  resolvedCliSessionId = cliSessionId;
851
- if (isCodex) {
852
- // Codex MCP manages threadId internally and ignores external session ids
853
- // (codex-mcp-process.ts:366-369) - the pool reset does NOT reset the codex
854
- // thread, so the stateless guarantee does not hold there. Loud, not silent.
855
- console.log('[AgentLoop] freshSession is a pool-level reset only on the codex backend - the codex thread persists (see TODO: codex report thread reset)');
856
- }
857
1023
  console.log(`[AgentLoop] [${isCodex ? 'codex' : 'claude'}] ${channelKey} (FRESH session - stateless lane)`);
858
1024
  }
859
1025
  else {
@@ -878,41 +1044,121 @@ class AgentLoop {
878
1044
  modelRunId: ownedModelRunId,
879
1045
  }), backgroundTasks);
880
1046
  }
1047
+ let nativeToolCallCount = 0;
1048
+ let nativeConsecutiveToolCalls = 0;
1049
+ let nativeLastToolName = '';
1050
+ const hostToolBridge = isCodex && this.isGatewayMode
1051
+ ? {
1052
+ tools: this.useCodeAct
1053
+ ? outerCodeActAllowed
1054
+ ? tool_registry_js_1.ToolRegistry.getHostToolDefinitions({ allowedTools: [index_js_1.CODE_ACT_MARKER] })
1055
+ : []
1056
+ : tool_registry_js_1.ToolRegistry.getHostToolDefinitions({
1057
+ allowedTools: options?.agentContext?.role.allowedTools,
1058
+ blockedTools: options?.agentContext?.role.blockedTools,
1059
+ disallowedTools: this.disallowedTools,
1060
+ viewer: options?.agentContext?.platform === 'viewer',
1061
+ }),
1062
+ execute: async (call) => {
1063
+ const callSignal = call.signal ?? new AbortController().signal;
1064
+ callSignal.throwIfAborted();
1065
+ if (nativeToolCallCount >= EMERGENCY_MAX_TURNS) {
1066
+ return {
1067
+ content: `Native tool call budget exceeded emergency maximum turns (${EMERGENCY_MAX_TURNS})`,
1068
+ isError: true,
1069
+ abort: true,
1070
+ };
1071
+ }
1072
+ const nextConsecutiveCount = call.name === nativeLastToolName ? nativeConsecutiveToolCalls + 1 : 1;
1073
+ if (nextConsecutiveCount >= MAX_CONSECUTIVE_SAME_TOOL) {
1074
+ return {
1075
+ content: `Infinite loop detected: Tool "${call.name}" called ${nextConsecutiveCount} times consecutively`,
1076
+ isError: true,
1077
+ abort: true,
1078
+ };
1079
+ }
1080
+ nativeToolCallCount += 1;
1081
+ nativeConsecutiveToolCalls = nextConsecutiveCount;
1082
+ nativeLastToolName = call.name;
1083
+ const toolUse = {
1084
+ type: 'tool_use',
1085
+ id: call.callId,
1086
+ name: call.name,
1087
+ input: call.input,
1088
+ };
1089
+ history.push({ role: 'assistant', content: [toolUse] });
1090
+ runScope.onTurn?.({
1091
+ turn,
1092
+ role: 'assistant',
1093
+ content: [toolUse],
1094
+ stopReason: 'tool_use',
1095
+ });
1096
+ const callExecutionContext = toolExecutionContext
1097
+ ? {
1098
+ ...toolExecutionContext,
1099
+ gatewayCallId: call.callId,
1100
+ signal: callSignal,
1101
+ }
1102
+ : null;
1103
+ const [toolResult] = await this.executeTools([toolUse], options?.stopAfterSuccessfulTools ?? [], callExecutionContext, runScope);
1104
+ callSignal.throwIfAborted();
1105
+ if (!toolResult) {
1106
+ return {
1107
+ content: `Native tool "${call.name}" returned no result`,
1108
+ isError: true,
1109
+ abort: true,
1110
+ };
1111
+ }
1112
+ history.push({ role: 'user', content: [toolResult] });
1113
+ runScope.onTurn?.({
1114
+ turn,
1115
+ role: 'user',
1116
+ content: [toolResult],
1117
+ });
1118
+ return {
1119
+ content: toolResult.content,
1120
+ isError: toolResult.is_error === true,
1121
+ stop: toolResult.is_error !== true &&
1122
+ (options?.stopAfterSuccessfulTools ?? []).includes(call.name),
1123
+ };
1124
+ },
1125
+ }
1126
+ : undefined;
881
1127
  let perCallSystemPrompt;
882
- if (options?.systemPrompt) {
883
- // Skip gateway tools if already embedded in systemPrompt (e.g. by MessageRouter).
884
- // '# Gateway Tools' matches both the doc's H1 title and any H2 form -
885
- // before this, only the '## Code-Act' section header (gateway-tools.md:87)
886
- // was accidentally keeping router-built prompts from double-injection.
887
- const alreadyHasTools = options.systemPrompt.includes('# Gateway Tools') ||
888
- options.systemPrompt.includes('# Code Execution') ||
889
- options.systemPrompt.includes('## Code-Act');
1128
+ if (options?.systemPrompt || (this.isGatewayMode && this.useCodeAct)) {
1129
+ let baseSystemPrompt = options?.systemPrompt ?? this.defaultSystemPrompt;
890
1130
  let gatewayToolsPrompt = '';
891
1131
  const isResumingSession = options?.resumeSession === true;
892
- if (this.isGatewayMode && !alreadyHasTools && !isResumingSession) {
893
- if (this.useCodeAct) {
894
- const allowedGatewayTools = resolveAllowedCodeActTools(options.agentContext?.role.allowedTools, options.agentContext?.role.blockedTools);
895
- const typeDefs = index_js_1.TypeDefinitionGenerator.generate(runScope.tier, allowedGatewayTools);
896
- const codeActBackend = this.backend === 'codex' || this.backend === 'codex-mcp'
897
- ? 'codex-mcp'
898
- : 'claude';
899
- gatewayToolsPrompt =
900
- (0, index_js_1.getCodeActInstructions)(codeActBackend, allowedGatewayTools) +
901
- '\n```typescript\n' +
902
- typeDefs +
903
- '\n```';
1132
+ if (this.isGatewayMode && this.useCodeAct) {
1133
+ baseSystemPrompt = stripTrailingCanonicalCodeActSection(stripGenericGatewayToolsCatalog(baseSystemPrompt));
1134
+ if (outerCodeActAllowed) {
1135
+ const policy = codeActPolicy;
1136
+ const typeDefs = index_js_1.TypeDefinitionGenerator.generate(policy);
1137
+ gatewayToolsPrompt = wrapGeneratedPromptSection('codeAct', (0, index_js_1.getCodeActInstructions)(isCodex ? 'codex' : 'claude', policy.names) +
1138
+ '\n```typescript\n' +
1139
+ typeDefs +
1140
+ '\n```');
904
1141
  }
905
- else {
906
- gatewayToolsPrompt = getGatewayToolsPrompt(this.disallowedTools);
1142
+ }
1143
+ else if (this.isGatewayMode && !isCodex) {
1144
+ baseSystemPrompt = stripDisabledCodeActGuidance(baseSystemPrompt);
1145
+ if (!isResumingSession) {
1146
+ // Non-CodeAct callers may already embed the generic gateway catalog.
1147
+ const alreadyHasTools = baseSystemPrompt.includes('# Gateway Tools') ||
1148
+ baseSystemPrompt.includes('# Code Execution') ||
1149
+ baseSystemPrompt.includes('## Code-Act');
1150
+ if (!alreadyHasTools) {
1151
+ gatewayToolsPrompt = wrapGeneratedPromptSection('gatewayTools', getGatewayToolsPrompt(this.disallowedTools));
1152
+ }
907
1153
  }
908
1154
  }
909
1155
  const fullPrompt = gatewayToolsPrompt
910
- ? `${options.systemPrompt}\n\n---\n\n${gatewayToolsPrompt}`
911
- : options.systemPrompt;
1156
+ ? `${baseSystemPrompt}\n\n---\n\n${gatewayToolsPrompt}`
1157
+ : baseSystemPrompt;
912
1158
  // Monitor and enforce prompt size
913
1159
  const monitor = new prompt_size_monitor_js_1.PromptSizeMonitor();
914
1160
  const runLayers = [
915
- { name: 'systemPrompt', content: options.systemPrompt, priority: 1 },
1161
+ { name: 'systemPrompt', content: baseSystemPrompt, priority: 1 },
916
1162
  ...(gatewayToolsPrompt
917
1163
  ? [{ name: 'gatewayTools', content: gatewayToolsPrompt, priority: 2 }]
918
1164
  : []),
@@ -927,14 +1173,14 @@ class AgentLoop {
927
1173
  if (enforceResult.truncatedLayers.length > 0) {
928
1174
  console.warn(`[AgentLoop] Truncated layers: ${enforceResult.truncatedLayers.join(', ')}`);
929
1175
  }
930
- const tBase = trimmed.find((l) => l.name === 'systemPrompt')?.content || options.systemPrompt;
1176
+ const tBase = trimmed.find((l) => l.name === 'systemPrompt')?.content || baseSystemPrompt;
931
1177
  const tTools = trimmed.find((l) => l.name === 'gatewayTools')?.content || '';
932
1178
  effectivePrompt = tTools ? `${tBase}\n\n---\n\n${tTools}` : tBase;
933
1179
  console.log(`[AgentLoop] System prompt truncated: ${fullPrompt.length} → ${effectivePrompt.length} chars`);
934
1180
  }
935
1181
  perCallSystemPrompt = effectivePrompt;
936
1182
  console.log(`[AgentLoop] Prepared systemPrompt for this call: ${effectivePrompt.length} chars ` +
937
- `(base: ${options.systemPrompt.length}, tools: ${gatewayToolsPrompt.length})`);
1183
+ `(base: ${baseSystemPrompt.length}, tools: ${gatewayToolsPrompt.length})`);
938
1184
  }
939
1185
  else {
940
1186
  perCallSystemPrompt = this.defaultSystemPrompt;
@@ -992,8 +1238,10 @@ class AgentLoop {
992
1238
  let piResult;
993
1239
  // Claude: First turn → --session-id (inject system prompt), subsequent → --resume
994
1240
  // Codex: resumeSession only controls threadId reset (false=new thread, true=continue)
995
- const shouldResume = !sessionIsNew || turn > 1;
996
- // Both Claude PersistentCLI and Codex MCP preserve context - only send new messages
1241
+ const shouldResume = isCodex
1242
+ ? turn > 1 || (options?.freshSession === true ? false : (options?.resumeSession ?? true))
1243
+ : !sessionIsNew || turn > 1;
1244
+ // Both Claude PersistentCLI and Codex app-server preserve context - only send new messages
997
1245
  const promptText = this.formatLastMessageOnly(history);
998
1246
  const promptStart = Date.now();
999
1247
  try {
@@ -1001,7 +1249,13 @@ class AgentLoop {
1001
1249
  model: options?.model,
1002
1250
  resumeSession: shouldResume,
1003
1251
  systemPrompt: perCallSystemPrompt,
1252
+ sessionKey: channelKey,
1253
+ sessionPolicyFingerprint: effectiveSessionPolicyFingerprint,
1004
1254
  sessionId: resolvedCliSessionId ?? undefined,
1255
+ // Per-run request timeout (operator worker runs); undefined leaves
1256
+ // the pool's construction-time default untouched (chat).
1257
+ requestTimeout: options?.requestTimeoutMs,
1258
+ hostToolBridge,
1005
1259
  });
1006
1260
  // Emit prompt latency metric
1007
1261
  this.onMetric?.('prompt_latency_ms', Date.now() - promptStart, {
@@ -1026,7 +1280,7 @@ class AgentLoop {
1026
1280
  errorMessage.includes('request_too_large') ||
1027
1281
  errorMessage.includes('context window') ||
1028
1282
  errorMessage.includes('context_length_exceeded');
1029
- if (isSessionNotFound || isSessionInUse || isPromptTooLong) {
1283
+ if (!isCodex && (isSessionNotFound || isSessionInUse || isPromptTooLong)) {
1030
1284
  const reason = isSessionNotFound
1031
1285
  ? 'not found in CLI'
1032
1286
  : isSessionInUse
@@ -1043,7 +1297,12 @@ class AgentLoop {
1043
1297
  model: options?.model,
1044
1298
  resumeSession: false, // Force new session
1045
1299
  systemPrompt: perCallSystemPrompt,
1300
+ sessionKey: channelKey,
1301
+ sessionPolicyFingerprint: effectiveSessionPolicyFingerprint,
1046
1302
  sessionId: newSessionId,
1303
+ // Carry the per-run timeout onto the reset session too.
1304
+ requestTimeout: options?.requestTimeoutMs,
1305
+ hostToolBridge,
1047
1306
  });
1048
1307
  // Prepend reset notice so user knows context was lost
1049
1308
  if (isPromptTooLong && piResult.response) {
@@ -1061,7 +1320,7 @@ class AgentLoop {
1061
1320
  const contentBlocks = [];
1062
1321
  let parsedToolCalls = [];
1063
1322
  // Parse tool_call / code_act blocks from text response (Gateway Tools mode ONLY)
1064
- if (this.isGatewayMode) {
1323
+ if (this.isGatewayMode && !isCodex) {
1065
1324
  parsedToolCalls = this.parseToolCallsFromText(piResult.response || '');
1066
1325
  // Code-Act: parse ```js blocks only if enabled
1067
1326
  if (this.useCodeAct) {
@@ -1106,11 +1365,8 @@ class AgentLoop {
1106
1365
  }
1107
1366
  console.log(`[AgentLoop] Detected ${toolUseBlocks.length} tool calls from MCP`);
1108
1367
  }
1109
- // Set stop_reason based on whether tools were requested
1110
- // In Gateway mode: check parsed tool calls; in MCP mode: check CLI tool blocks
1111
- const hasToolUse = this.isGatewayMode
1112
- ? parsedToolCalls.length > 0
1113
- : ('hasToolUse' in piResult ? piResult.hasToolUse : false) || false;
1368
+ // Both parsed gateway calls and native/MCP tool blocks share executeTools.
1369
+ const hasToolUse = contentBlocks.some((block) => block.type === 'tool_use');
1114
1370
  // eslint-disable-next-line prefer-const
1115
1371
  response = {
1116
1372
  id: `msg_${Date.now()}`,
@@ -1142,7 +1398,7 @@ class AgentLoop {
1142
1398
  }
1143
1399
  }
1144
1400
  // Track tokens in session pool for auto-reset at 80% context
1145
- const tokenBackend = this.backend === 'codex' || this.backend === 'codex-mcp' ? 'codex-mcp' : 'claude';
1401
+ const tokenBackend = this.backend === 'codex' ? 'codex' : 'claude';
1146
1402
  const tokenStatus = this.sessionPool.updateTokens(channelKey, response.usage.input_tokens, tokenBackend);
1147
1403
  // PreCompact: inject compaction summary when approaching context limit
1148
1404
  if (tokenStatus.nearThreshold && this.preCompactHandler && !runScope.preCompactInjected) {
@@ -1342,60 +1598,36 @@ class AgentLoop {
1342
1598
  for (const toolUse of toolUseBlocks) {
1343
1599
  let result;
1344
1600
  let isError = false;
1601
+ const executionToolName = toolUse.name === CODE_ACT_MCP_COMPAT_NAME ? index_js_1.CODE_ACT_MARKER : toolUse.name;
1345
1602
  // Notify stream: tool execution starting
1346
- runScope.streamCallbacks?.onToolUse?.(toolUse.name, toolUse.input);
1603
+ runScope.streamCallbacks?.onToolUse?.(executionToolName, toolUse.input);
1347
1604
  const toolStart = Date.now();
1348
1605
  try {
1349
- // Code-Act: execute JS code in sandbox
1350
- // Match both gateway-parsed 'code_act' and MCP-prefixed 'mcp__code-act__code_act'
1351
- if (toolUse.name === index_js_1.CODE_ACT_MARKER || toolUse.name === 'mcp__code-act__code_act') {
1352
- const codeInput = toolUse.input;
1353
- const code = typeof codeInput?.code === 'string' ? codeInput.code : '';
1354
- const codeActResult = code
1355
- ? await this.executeCodeAct(code, runScope.tier, modelToolContext, runScope)
1356
- : {
1357
- success: false,
1358
- error: {
1359
- name: 'ValidationError',
1360
- message: 'Missing or invalid "code" field in code_act input',
1361
- },
1362
- logs: [],
1363
- metrics: { durationMs: 0, hostCallCount: 0, memoryUsedBytes: 0 },
1364
- };
1365
- result = JSON.stringify(codeActResult, null, 2);
1366
- if (!codeActResult.success) {
1367
- isError = true;
1368
- }
1369
- runScope.onToolUse?.(toolUse.name, toolUse.input, codeActResult);
1370
- runScope.streamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, isError);
1606
+ // PreToolUse: search MAMA for contracts before Write operations
1607
+ let contractContext = '';
1608
+ if (executionToolName === 'Write' && toolUse.input) {
1609
+ contractContext = await this.searchContractsForTool(executionToolName, toolUse.input, reactiveInternalContext);
1371
1610
  }
1372
- else {
1373
- // PreToolUse: search MAMA for contracts before Write operations
1374
- let contractContext = '';
1375
- if (toolUse.name === 'Write' && toolUse.input) {
1376
- contractContext = await this.searchContractsForTool(toolUse.name, toolUse.input, reactiveInternalContext);
1377
- }
1378
- const toolResult = await this.mcpExecutor.execute(toolUse.name, toolUse.input, modelToolContext ?? undefined);
1379
- result = JSON.stringify(toolResult, null, 2);
1380
- // Check if tool execution failed
1381
- const hasSuccess = 'success' in toolResult;
1382
- const toolFailed = hasSuccess && !toolResult.success;
1383
- if (toolFailed) {
1384
- isError = true;
1385
- }
1386
- if (contractContext) {
1387
- result = `${contractContext}\n\n---\n\n${result}`;
1388
- }
1389
- // Notify tool use callback
1390
- runScope.onToolUse?.(toolUse.name, toolUse.input, toolResult);
1391
- // PostToolUse: auto-extract contracts (fire-and-forget)
1392
- this.postToolHandler?.processInBackground(toolUse.name, toolUse.input, toolResult, reactiveInternalContext);
1393
- // Notify stream: tool completed (check actual status)
1394
- runScope.streamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, isError);
1611
+ const toolResult = await this.mcpExecutor.execute(executionToolName, toolUse.input, modelToolContext ?? undefined);
1612
+ result = JSON.stringify(toolResult, null, 2);
1613
+ // Check if tool execution failed
1614
+ const hasSuccess = 'success' in toolResult;
1615
+ const toolFailed = hasSuccess && !toolResult.success;
1616
+ if (toolFailed) {
1617
+ isError = true;
1618
+ }
1619
+ if (contractContext) {
1620
+ result = `${contractContext}\n\n---\n\n${result}`;
1395
1621
  }
1622
+ // Notify tool use callback
1623
+ runScope.onToolUse?.(executionToolName, toolUse.input, toolResult);
1624
+ // PostToolUse: auto-extract contracts (fire-and-forget)
1625
+ this.postToolHandler?.processInBackground(executionToolName, toolUse.input, toolResult, reactiveInternalContext);
1626
+ // Notify stream: tool completed (check actual status)
1627
+ runScope.streamCallbacks?.onToolComplete?.(executionToolName, toolUse.id, isError);
1396
1628
  // Emit tool execution metric
1397
1629
  this.onMetric?.('tool_duration_ms', Date.now() - toolStart, {
1398
- tool: toolUse.name,
1630
+ tool: executionToolName,
1399
1631
  error: String(isError),
1400
1632
  });
1401
1633
  }
@@ -1403,13 +1635,13 @@ class AgentLoop {
1403
1635
  isError = true;
1404
1636
  result = error instanceof Error ? error.message : String(error);
1405
1637
  // Notify tool use callback with error
1406
- runScope.onToolUse?.(toolUse.name, toolUse.input, { error: result });
1638
+ runScope.onToolUse?.(executionToolName, toolUse.input, { error: result });
1407
1639
  this.onMetric?.('tool_duration_ms', Date.now() - toolStart, {
1408
- tool: toolUse.name,
1640
+ tool: executionToolName,
1409
1641
  error: 'true',
1410
1642
  });
1411
1643
  // Notify stream: tool completed with error
1412
- runScope.streamCallbacks?.onToolComplete?.(toolUse.name, toolUse.id, true);
1644
+ runScope.streamCallbacks?.onToolComplete?.(executionToolName, toolUse.id, true);
1413
1645
  }
1414
1646
  results.push({
1415
1647
  type: 'tool_result',
@@ -1417,7 +1649,7 @@ class AgentLoop {
1417
1649
  content: result,
1418
1650
  is_error: isError,
1419
1651
  });
1420
- if (!isError && stopAfterSuccessfulTools.includes(toolUse.name)) {
1652
+ if (!isError && stopAfterSuccessfulTools.includes(executionToolName)) {
1421
1653
  break;
1422
1654
  }
1423
1655
  }
@@ -1507,50 +1739,6 @@ class AgentLoop {
1507
1739
  }
1508
1740
  return blocks;
1509
1741
  }
1510
- /**
1511
- * Execute Code-Act JS code in a sandboxed QuickJS environment
1512
- */
1513
- async executeCodeAct(code, tier = 1, executionContext = null, runScope = { tier: 1 }) {
1514
- try {
1515
- const sandbox = new index_js_1.CodeActSandbox();
1516
- const bridgeContext = withExecutionSurface(executionContext, 'code_act');
1517
- const bridge = new index_js_1.HostBridge(this.mcpExecutor, undefined, bridgeContext);
1518
- bridge.onToolUse = (toolName, input, result) => {
1519
- if (result === undefined) {
1520
- // Tool starting — surface to stream
1521
- runScope.streamCallbacks?.onToolUse?.(toolName, input);
1522
- }
1523
- if (result !== undefined) {
1524
- // Tool completed — notify callback
1525
- runScope.onToolUse?.(toolName, input, result);
1526
- const isError = typeof result === 'object' &&
1527
- result !== null &&
1528
- 'success' in result &&
1529
- !result.success;
1530
- runScope.streamCallbacks?.onToolComplete?.(toolName, `code_act_sub_${Date.now()}`, isError);
1531
- }
1532
- };
1533
- bridge.injectInto(sandbox, tier);
1534
- const result = await sandbox.execute(code);
1535
- if (result.logs.length > 0) {
1536
- console.log(`[CodeAct] console output: ${result.logs.join('\n')}`);
1537
- }
1538
- return result;
1539
- }
1540
- catch (err) {
1541
- const message = err instanceof Error ? err.message : String(err);
1542
- console.error(`[CodeAct] Sandbox initialization failed: ${message}`);
1543
- return {
1544
- success: false,
1545
- error: {
1546
- name: 'SandboxError',
1547
- message: `Failed to initialize Code-Act sandbox: ${message}`,
1548
- },
1549
- logs: [],
1550
- metrics: { durationMs: 0, hostCallCount: 0, memoryUsedBytes: 0 },
1551
- };
1552
- }
1553
- }
1554
1742
  /**
1555
1743
  * Remove tool_call and code_act blocks from text (to avoid duplication in response)
1556
1744
  */
@@ -1685,7 +1873,7 @@ class AgentLoop {
1685
1873
  this.stopped = true;
1686
1874
  try {
1687
1875
  // Stop the model runner
1688
- this.agent.stop();
1876
+ await this.agent.stop();
1689
1877
  const waitUntil = Date.now() + 5000;
1690
1878
  while (this.laneManager.getTotalQueueSize() > 0 && Date.now() < waitUntil) {
1691
1879
  await new Promise((resolve) => setTimeout(resolve, 50));