maka-agent 0.2.0-dev.46.20260922 → 0.2.0-dev.48.20260923

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 (131) hide show
  1. package/DISCLAIMER-WIP +6 -1
  2. package/README.md +6 -1
  3. package/dist/acp/maka-acp-agent.js +1 -1
  4. package/dist/acp/session-mcp.js +212 -0
  5. package/dist/acp/session-registry.js +52 -8
  6. package/dist/acp/stdio-server.js +3 -0
  7. package/dist/cli-core.js +1 -1
  8. package/dist/mcp-capability-provider.js +6 -1
  9. package/dist/mcp-capability-publication.js +139 -0
  10. package/dist/pi-tui-layout.js +46 -17
  11. package/dist/pi-tui-pickers.js +25 -14
  12. package/dist/pi-tui-runner.js +24 -15
  13. package/dist/runtime-host-capability-provider-command.js +25 -6
  14. package/dist/runtime-host-service-manager.js +26 -2
  15. package/dist/tui-context-refresh.js +15 -26
  16. package/dist/tui-mcp-control.js +66 -112
  17. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  18. package/node_modules/@maka/acp-executor-plugin/dist/acp-errors.js +26 -0
  19. package/node_modules/@maka/acp-executor-plugin/dist/acp-filesystem.js +103 -0
  20. package/node_modules/@maka/acp-executor-plugin/dist/acp-process.js +119 -0
  21. package/node_modules/@maka/acp-executor-plugin/dist/acp-projection.js +146 -0
  22. package/node_modules/@maka/acp-executor-plugin/dist/index.js +653 -0
  23. package/node_modules/@maka/acp-executor-plugin/dist/plugin.mjs +24446 -0
  24. package/node_modules/@maka/acp-executor-plugin/package.json +15 -0
  25. package/node_modules/@maka/antigravity-acp-plugin/dist/index.js +88 -0
  26. package/node_modules/@maka/antigravity-acp-plugin/dist/model-presentation.js +53 -0
  27. package/node_modules/@maka/antigravity-acp-plugin/dist/plugin.mjs +115 -0
  28. package/node_modules/@maka/antigravity-acp-plugin/package.json +15 -0
  29. package/node_modules/@maka/core/dist/client-capability-grant.js +2 -2
  30. package/node_modules/@maka/core/dist/executor-catalog.js +106 -0
  31. package/node_modules/@maka/core/dist/mcp.js +4 -1
  32. package/node_modules/@maka/core/dist/model-metadata.js +28 -1
  33. package/node_modules/@maka/core/dist/model-thinking.js +36 -1
  34. package/node_modules/@maka/core/dist/provider-registry.js +0 -1
  35. package/node_modules/@maka/core/dist/refresh-read-coordinator.js +62 -0
  36. package/node_modules/@maka/core/dist/runtime-event-store.js +25 -0
  37. package/node_modules/@maka/core/dist/runtime-invocation.js +5 -0
  38. package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +4 -0
  39. package/node_modules/@maka/core/dist/runtime-policy/credential-vault-codec.js +6 -0
  40. package/node_modules/@maka/core/dist/runtime-policy/policy-codec.js +21 -0
  41. package/node_modules/@maka/core/dist/session.js +19 -1
  42. package/node_modules/@maka/core/dist/settings.js +3 -0
  43. package/node_modules/@maka/core/dist/tool-ledger-scanner.js +365 -104
  44. package/node_modules/@maka/core/dist/turn-origin.js +14 -0
  45. package/node_modules/@maka/core/package.json +3 -0
  46. package/node_modules/@maka/mcp/dist/credential-coordinator.js +6 -1
  47. package/node_modules/@maka/mcp/dist/index.js +31 -5
  48. package/node_modules/@maka/mcp/dist/oauth.js +54 -6
  49. package/node_modules/@maka/runtime/dist/agent-run-inspect.js +1 -1
  50. package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +15 -60
  51. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +18 -0
  52. package/node_modules/@maka/runtime/dist/apply-patch-profile.js +64 -14
  53. package/node_modules/@maka/runtime/dist/builtin-tools.js +3 -2
  54. package/node_modules/@maka/runtime/dist/codex-v4a-patch.js +18 -0
  55. package/node_modules/@maka/runtime/dist/conversation-copy.js +56 -1
  56. package/node_modules/@maka/runtime/dist/mcp-tools.js +9 -2
  57. package/node_modules/@maka/runtime/dist/model-adapter.js +80 -3
  58. package/node_modules/@maka/runtime/dist/model-runtime.js +6 -0
  59. package/node_modules/@maka/runtime/dist/openai-apply-patch.js +5 -0
  60. package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +122 -3
  61. package/node_modules/@maka/runtime/dist/plugin-executor-service.js +212 -16
  62. package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +2 -2
  63. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -0
  64. package/node_modules/@maka/runtime/dist/session-manager.js +184 -126
  65. package/node_modules/@maka/runtime/dist/shell-run-manager.js +3 -0
  66. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +29 -4
  67. package/node_modules/@maka/runtime-host/dist/candidate-entry.js +8 -5
  68. package/node_modules/@maka/runtime-host/dist/client/capability-provider-service.js +2 -0
  69. package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +59 -20
  70. package/node_modules/@maka/runtime-host/dist/client/connection.js +6 -4
  71. package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
  72. package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +8 -1
  73. package/node_modules/@maka/runtime-host/dist/client/reconnecting-connection.js +4 -4
  74. package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +6 -1
  75. package/node_modules/@maka/runtime-host/dist/composition-identity.js +19 -0
  76. package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +20 -3
  77. package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +8 -5
  78. package/node_modules/@maka/runtime-host/dist/protocol/index.js +20 -2
  79. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +34 -0
  80. package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +75 -5
  81. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +1 -5
  82. package/node_modules/@maka/runtime-host/dist/server/builtin-external-agent-plugins.js +188 -0
  83. package/node_modules/@maka/runtime-host/dist/server/candidate.js +12 -7
  84. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +234 -70
  85. package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +14 -8
  86. package/node_modules/@maka/runtime-host/dist/server/client-capability-recovery.js +44 -46
  87. package/node_modules/@maka/runtime-host/dist/server/context-coordinator.js +2 -0
  88. package/node_modules/@maka/runtime-host/dist/server/execution-composition-factory.js +1 -1
  89. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +188 -21
  90. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +6 -0
  91. package/node_modules/@maka/runtime-host/dist/server/extension-bundle.js +4 -0
  92. package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +29 -7
  93. package/node_modules/@maka/runtime-host/dist/server/host-composition-source.js +47 -0
  94. package/node_modules/@maka/runtime-host/dist/server/host-composition.js +1 -29
  95. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +2 -9
  96. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +75 -9
  97. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +5 -1
  98. package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +45 -0
  99. package/node_modules/@maka/runtime-host/dist/server/jev-routing-model.js +157 -0
  100. package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -2
  101. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +4 -0
  102. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +11 -1
  103. package/node_modules/@maka/runtime-host/dist/server/process-lifecycle.js +1 -1
  104. package/node_modules/@maka/runtime-host/dist/server/process-termination-error.js +27 -0
  105. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +91 -12
  106. package/node_modules/@maka/runtime-host/dist/server/runtime-policy-coordinator.js +2 -0
  107. package/node_modules/@maka/runtime-host/dist/server/session-bundle-coordinator.js +4 -1
  108. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +128 -13
  109. package/node_modules/@maka/runtime-host/dist/server/session-effect-coordinator.js +12 -6
  110. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +2 -2
  111. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +33 -0
  112. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +4 -0
  113. package/node_modules/@maka/runtime-host/dist/server/workhub-result-coordinator.js +173 -0
  114. package/node_modules/@maka/runtime-host/dist/server/workhub-result-runtime.js +293 -0
  115. package/node_modules/@maka/runtime-host/package.json +2 -0
  116. package/node_modules/@maka/storage/dist/agent-run-store-contract.js +8 -0
  117. package/node_modules/@maka/storage/dist/execution-stores.js +15 -3
  118. package/node_modules/@maka/storage/dist/mcp-config-store.js +91 -2
  119. package/node_modules/@maka/storage/dist/operational-state-store.js +15 -1
  120. package/node_modules/@maka/storage/dist/plan-authority.js +2 -0
  121. package/node_modules/@maka/storage/dist/plan-store.js +30 -0
  122. package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
  123. package/node_modules/@maka/storage/dist/runtime-policy/credential-vault-document.js +4 -1
  124. package/node_modules/@maka/storage/dist/runtime-policy/policy-document.js +2 -0
  125. package/node_modules/@maka/storage/dist/session-store-values.js +16 -4
  126. package/node_modules/@maka/storage/dist/shell-run-authority.js +1 -0
  127. package/node_modules/@maka/storage/dist/shell-run-store.js +15 -0
  128. package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +74 -1
  129. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +724 -84
  130. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +16 -0
  131. package/package.json +1 -1
@@ -23,6 +23,7 @@ import { decodePersistedToolResultContent } from '@maka/core/tool-result-record-
23
23
  import { isEmittedAgentRunEventType } from '@maka/core/agent-run';
24
24
  import { decodeModelCallAttempt, MODEL_CALL_ATTEMPT_EVENT_TYPE, } from '@maka/core/model-call-attempt';
25
25
  import { TOOL_RECOVERY_DECISION_FACT_KIND } from '@maka/core/tool-recovery-fact';
26
+ import { canonicalToolArgsHash } from '@maka/core/tool-args-identity';
26
27
  import { buildHistoryCompactCheckpoint, historyCompactSourceDigest, matchHistoryCompactCheckpointPrefix, validateHistoryCompactCheckpointShape, } from './history-compact-checkpoint.js';
27
28
  import { findCheckpointSummaryDefect } from './history-compact-summary-validation.js';
28
29
  import { isHistoryCompactContentEvent } from './history-compaction.js';
@@ -281,6 +282,20 @@ function assertConversationRuntimeLedgerCopySupported(run, runtimeEvents) {
281
282
  error.code = 'branch_runtime_fact_rewrite_unsupported';
282
283
  throw error;
283
284
  }
285
+ /**
286
+ * Mirrors the T1 scanner's identity semantics: a call whose args are not
287
+ * strict JSON cannot authenticate, which the scanner surfaces as a
288
+ * `canonical_args_hash_conflict` rather than an exception — so the copy pass
289
+ * must see `undefined` there, not a thrown error.
290
+ */
291
+ const tryCanonicalToolArgsHash = (toolName, args) => {
292
+ try {
293
+ return canonicalToolArgsHash(toolName, args);
294
+ }
295
+ catch {
296
+ return undefined;
297
+ }
298
+ };
284
299
  export async function cloneConversationRuntimeLedger(input) {
285
300
  if (input.plan.sourceSessionId !== input.referenceMap.sourceSessionId) {
286
301
  throw new Error('Conversation copy plan does not belong to the source Session');
@@ -386,10 +401,22 @@ export async function cloneConversationRuntimeLedger(input) {
386
401
  };
387
402
  for (const sourceId of clonedEventBySourceId.keys())
388
403
  finishTarget(sourceId);
404
+ // The identity rewrite changes a copied call's canonical args, so the paired
405
+ // dispatch's recorded hash stops authenticating them and the copied ledger
406
+ // fails its own T1 re-scan. Collect the source and rewritten identities per
407
+ // (invocation, tool call) and re-authenticate the dispatches after the
408
+ // rewrite pass — but only when the source dispatch actually authenticated
409
+ // the source call. Stamping the rewritten identity over a mismatched hash
410
+ // would launder an already-corrupt source ledger into a valid-looking copy
411
+ // instead of preserving the corruption for the re-scan to reject (#5466
412
+ // review). Identity derivation mirrors the scanner's semantics: args that
413
+ // are not strict JSON cannot authenticate at all.
414
+ const rewrittenArgsIdentities = new Map();
389
415
  for (const event of clonedEventBySourceId.values()) {
390
416
  if (event.content?.kind === 'function_call' && event.content.name === 'Read') {
391
417
  const args = event.content.args;
392
- if (args && typeof args === 'object' && 'path' in args && typeof args.path === 'string')
418
+ if (args && typeof args === 'object' && 'path' in args && typeof args.path === 'string') {
419
+ const sourceArgsHash = tryCanonicalToolArgsHash(event.content.name, args);
393
420
  event.content = {
394
421
  ...event.content,
395
422
  args: {
@@ -397,19 +424,47 @@ export async function cloneConversationRuntimeLedger(input) {
397
424
  ...rewriteReadInput(args, references, clonedEventBySourceId),
398
425
  },
399
426
  };
427
+ rewrittenArgsIdentities.set(`${event.invocationId}\u0000${event.content.id}`, {
428
+ sourceArgsHash,
429
+ rewrittenArgsHash: tryCanonicalToolArgsHash(event.content.name, event.content.args),
430
+ });
431
+ }
400
432
  }
401
433
  if (event.content?.kind === 'text')
402
434
  event.content.text = rewriteCopiedText(event.content.text, references, clonedEventBySourceId);
403
435
  if (event.content?.kind === 'function_call' && event.content.name === 'ArchiveRead') {
404
436
  const args = event.content.args;
405
437
  if (args && typeof args === 'object' && 'ref' in args && typeof args.ref === 'string') {
438
+ const sourceArgsHash = tryCanonicalToolArgsHash(event.content.name, args);
406
439
  event.content = {
407
440
  ...event.content,
408
441
  args: { ...args, ref: rewriteLedgerArchiveText(args.ref, references) },
409
442
  };
443
+ rewrittenArgsIdentities.set(`${event.invocationId}\u0000${event.content.id}`, {
444
+ sourceArgsHash,
445
+ rewrittenArgsHash: tryCanonicalToolArgsHash(event.content.name, event.content.args),
446
+ });
410
447
  }
411
448
  }
412
449
  }
450
+ if (rewrittenArgsIdentities.size > 0) {
451
+ for (const event of clonedEventBySourceId.values()) {
452
+ const dispatch = event.actions?.toolDispatch;
453
+ if (dispatch?.canonicalArgsHash === undefined)
454
+ continue;
455
+ const identity = rewrittenArgsIdentities.get(`${event.invocationId}\u0000${dispatch.providerToolCallId}`);
456
+ if (identity === undefined)
457
+ continue;
458
+ if (identity.sourceArgsHash !== dispatch.canonicalArgsHash)
459
+ continue;
460
+ if (identity.rewrittenArgsHash === undefined)
461
+ continue;
462
+ event.actions = {
463
+ ...event.actions,
464
+ toolDispatch: { ...dispatch, canonicalArgsHash: identity.rewrittenArgsHash },
465
+ };
466
+ }
467
+ }
413
468
  const preparedPlans = flattenedPlans.map((plan) => {
414
469
  const runId = runIds.get(plan.run.runId);
415
470
  const clonedOperationalEvents = plan.operationalEvents.flatMap((event) => {
@@ -136,13 +136,20 @@ function mcpToolDescription(descriptor) {
136
136
  }
137
137
  export function mcpProxyToolName(serverId, toolName) {
138
138
  const raw = `mcp__${sanitizeNamePart(serverId)}__${sanitizeNamePart(toolName)}`;
139
- if (raw.length <= MAX_PROVIDER_TOOL_NAME)
139
+ // Preserve existing simple names; hash identities whose spelling or separators are ambiguous.
140
+ if (raw.length <= MAX_PROVIDER_TOOL_NAME &&
141
+ sanitizeNamePart(serverId) === serverId &&
142
+ sanitizeNamePart(toolName) === toolName &&
143
+ !serverId.includes('__') &&
144
+ !toolName.includes('__'))
140
145
  return raw;
141
146
  const hash = createHash('sha256')
142
147
  .update(`${serverId}\0${toolName}`)
143
148
  .digest('hex')
144
149
  .slice(0, HASH_CHARS);
145
- return `${raw.slice(0, MAX_PROVIDER_TOOL_NAME - HASH_CHARS - 2)}__${hash}`;
150
+ // A truncated name must not become another identity's unmodified name.
151
+ const hashed = `mcp_h__${sanitizeNamePart(serverId)}__${sanitizeNamePart(toolName)}`;
152
+ return `${hashed.slice(0, MAX_PROVIDER_TOOL_NAME - HASH_CHARS - 2)}__${hash}`;
146
153
  }
147
154
  function sanitizeNamePart(value) {
148
155
  const sanitized = value
@@ -22,13 +22,14 @@ import { providerAuthRequiresSecret, } from '@maka/core/llm-connections';
22
22
  import { lookupModelMetadata } from '@maka/core/model-metadata';
23
23
  import { rawFinishReasonString } from './model-protocol.js';
24
24
  import { resolveModelRuntime } from './model-runtime.js';
25
+ import { resolveSelectedModelContextWindow } from './context-budget-policy.js';
25
26
  import { plaintextResponsesReasoningProviderOptions, withoutMakaResponsesState, safePlaintextResponsesReasoningItemId, } from './responses-reasoning-state.js';
26
27
  import { classifyError, providerModelFailure } from './provider-error-classification.js';
27
28
  import { withProviderStreamTracking, } from './provider-request-telemetry.js';
28
29
  import { createOpenAiChatReasoningTransportState, openAiChatReasoningFieldProviderOptions, restoreOpenAiChatEmptyReasoning, } from './openai-chat-reasoning-transport.js';
29
30
  import { mergeOpenAiResponsesProviderOptions, planOpenAiResponsesContinuation, } from './openai-responses-continuation.js';
30
31
  import { createOpenAiResponsesTransportState, OPENAI_RESPONSES_LANE_HEADER, } from './openai-responses-websocket.js';
31
- import { openAiApplyPatchProviderTool } from './openai-apply-patch.js';
32
+ import { openAiApplyPatchProviderTool, codexApplyPatchProviderTool } from './openai-apply-patch.js';
32
33
  import { TOOL_SEARCH_NAME, TOOL_SEARCH_PROVIDER_NAME } from './tool-availability.js';
33
34
  export class ModelAdapter {
34
35
  input;
@@ -97,12 +98,40 @@ export class ModelAdapter {
97
98
  maxOutputTokens() {
98
99
  return selectedModelMaxOutputTokens(this.input.connection, this.input.modelId, this.input.providerOptions, this.runtime);
99
100
  }
101
+ /**
102
+ * Keep a provider-derived output limit from making a resumed request
103
+ * impossible. The token count is the last request the provider accepted, so
104
+ * it is a conservative lower bound for the next request. Leave a small
105
+ * amount of room for newly appended user/tool content because Runtime does
106
+ * not estimate the final prompt locally.
107
+ */
108
+ maxOutputTokensForInput(knownInputTokens) {
109
+ const outputLimit = this.maxOutputTokens();
110
+ if (outputLimit === undefined ||
111
+ knownInputTokens === undefined ||
112
+ !Number.isFinite(knownInputTokens) ||
113
+ knownInputTokens < 0) {
114
+ return outputLimit;
115
+ }
116
+ const contextWindow = resolveSelectedModelContextWindow(this.input.connection, this.input.modelId);
117
+ if (contextWindow === undefined)
118
+ return outputLimit;
119
+ const thinkingBudget = this.runtime.wire === 'anthropic-messages'
120
+ ? fixedAnthropicThinkingBudget(this.input.providerOptions)
121
+ : 0;
122
+ const available = contextWindow - Math.ceil(knownInputTokens) - CONTEXT_INPUT_GROWTH_HEADROOM - thinkingBudget;
123
+ // Do not turn a near-window request into a one-token success. A useful
124
+ // floor deliberately lets the provider reject the request, which keeps
125
+ // the existing reactive compaction path in control of recovery.
126
+ return Math.max(Math.min(outputLimit, CONTEXT_RECOVERY_OUTPUT_FLOOR), Math.min(outputLimit, available));
127
+ }
100
128
  async startStream(input) {
101
129
  const ai = await import('ai').catch((err) => {
102
130
  throw new Error(`Failed to load 'ai' package. Run \`npm install ai\`. Inner: ${err.message}`);
103
131
  });
104
132
  const { streamText, wrapLanguageModel } = ai;
105
- const maxOutputTokens = selectedModelMaxOutputTokens(this.input.connection, this.input.modelId, this.input.providerOptions, this.runtime);
133
+ const maxOutputTokens = input.maxOutputTokens ??
134
+ selectedModelMaxOutputTokens(this.input.connection, this.input.modelId, this.input.providerOptions, this.runtime);
106
135
  let settleAccounting;
107
136
  const terminalModel = withProviderFinishBoundary(input.model, wrapLanguageModel);
108
137
  const trackedModel = input.providerRequestTracker
@@ -127,7 +156,7 @@ export class ModelAdapter {
127
156
  sdkTools[TOOL_SEARCH_PROVIDER_NAME] = sdkTools[TOOL_SEARCH_NAME];
128
157
  delete sdkTools[TOOL_SEARCH_NAME];
129
158
  }
130
- const fullMessages = input.messages;
159
+ const fullMessages = this.runtime.wire === 'openai-chat' ? lowerChatToolImages(input.messages) : input.messages;
131
160
  const responsesLane = input.continuationKey && usesNativeOpenAiResponses(this.input.connection, this.runtime)
132
161
  ? input.continuationKey
133
162
  : undefined;
@@ -441,6 +470,12 @@ function failedStepOutcome(failure, usage) {
441
470
  continuation: 'none',
442
471
  };
443
472
  }
473
+ /**
474
+ * A persisted provider count describes the preceding request, not the new
475
+ * user message or tool result that may be appended after a restart.
476
+ */
477
+ const CONTEXT_INPUT_GROWTH_HEADROOM = 8_000;
478
+ const CONTEXT_RECOVERY_OUTPUT_FLOOR = 8_000;
444
479
  function selectedModelMaxOutputTokens(connection, modelId, providerOptions, runtime) {
445
480
  const anthropicMessages = runtime.wire === 'anthropic-messages';
446
481
  const kimiOpenAiChat = connection.providerType === 'kimi-coding-plan' && runtime.wire === 'openai-chat';
@@ -835,6 +870,46 @@ function translateChunk(chunk, openAiChatReasoningTransportState, runtime, runti
835
870
  return [];
836
871
  }
837
872
  }
873
+ function lowerChatToolImages(messages) {
874
+ const result = [];
875
+ let images = [];
876
+ const flush = () => {
877
+ if (images.length === 0)
878
+ return;
879
+ result.push({ role: 'user', content: images });
880
+ images = [];
881
+ };
882
+ for (const message of messages) {
883
+ if (message.role !== 'tool') {
884
+ flush();
885
+ result.push(message);
886
+ continue;
887
+ }
888
+ result.push({
889
+ ...message,
890
+ content: message.content.map((part) => {
891
+ if (part.type !== 'tool-result' || part.output.type !== 'content')
892
+ return part;
893
+ return {
894
+ ...part,
895
+ output: {
896
+ ...part.output,
897
+ value: part.output.value.map((content) => {
898
+ if (content.type !== 'file' || !content.mediaType.startsWith('image/'))
899
+ return content;
900
+ images.push({ type: 'text', text: `Image from tool ${part.toolName} (${part.toolCallId}):` }, content);
901
+ return { type: 'text', text: 'Image supplied below.' };
902
+ }),
903
+ },
904
+ };
905
+ }),
906
+ });
907
+ }
908
+ // Chat tool messages are text-only; attach images after the whole result group
909
+ // so an assistant's parallel tool calls stay paired before the next user message.
910
+ flush();
911
+ return result;
912
+ }
838
913
  /**
839
914
  * OpenAI Responses reserves the provider name `tool_search`. Keep Maka's
840
915
  * persisted/history name intact and translate only the provider-bound copy.
@@ -898,6 +973,8 @@ function compileProviderTool(tool) {
898
973
  switch (tool.kind) {
899
974
  case 'openai-apply-patch':
900
975
  return openAiApplyPatchProviderTool;
976
+ case 'codex-apply-patch':
977
+ return codexApplyPatchProviderTool;
901
978
  case 'openai-web-search':
902
979
  return openai.tools.webSearch({
903
980
  ...(tool.searchContextSize ? { searchContextSize: tool.searchContextSize } : {}),
@@ -19,6 +19,7 @@
19
19
  import { PROVIDER_REGISTRY, effectiveBaseUrl, } from '@maka/core/llm-connections';
20
20
  import { lookupModelMetadata, lookupModelRuntimeOverride, openAiAdapterApiProtocol, } from '@maka/core/model-metadata';
21
21
  import { isRetiredProvider } from '@maka/core/provider-registry';
22
+ import { modelOverride } from '@maka/core/model-thinking';
22
23
  import { anthropicV1BaseUrl, googleV1BetaBaseUrl, openAiResponsesBaseUrl, } from './provider-urls.js';
23
24
  import { resolveApplyPatchProfile } from './apply-patch-profile.js';
24
25
  export function resolveModelRuntime(connection, modelId) {
@@ -76,6 +77,11 @@ export function resolveModelRuntime(connection, modelId) {
76
77
  applyPatchProfile: resolveApplyPatchProfile({
77
78
  wire,
78
79
  applyPatchProtocol: adapter.applyPatchProtocol,
80
+ enabled: modelOverride(connection, modelId)?.applyPatch,
81
+ customTools: wire === 'openai-responses' &&
82
+ (connection.providerType === 'openai' || connection.providerType === 'openai-codex') &&
83
+ replay.kind === 'responses' &&
84
+ replay.contract.adapter === 'openai',
79
85
  }, modelId),
80
86
  };
81
87
  }
@@ -17,6 +17,11 @@
17
17
  * under the License.
18
18
  */
19
19
  import { openai } from '@ai-sdk/openai';
20
+ import { CODEX_PATCH_DESCRIPTION, CODEX_PATCH_GRAMMAR } from './codex-v4a-patch.js';
21
+ export const codexApplyPatchProviderTool = openai.tools.customTool({
22
+ description: `${CODEX_PATCH_DESCRIPTION}\nPass raw patch text, not JSON.`,
23
+ format: { type: 'grammar', syntax: 'lark', definition: CODEX_PATCH_GRAMMAR },
24
+ });
20
25
  export const openAiApplyPatchProviderTool = openai.tools.applyPatch({});
21
26
  const inputSchema = openAiApplyPatchProviderTool.inputSchema;
22
27
  export const openAiApplyPatchInputSchema = typeof inputSchema === 'function' ? inputSchema() : inputSchema;
@@ -17,13 +17,17 @@
17
17
  * under the License.
18
18
  */
19
19
  import { randomUUID } from 'node:crypto';
20
+ import { redactSecrets } from '@maka/core/redaction';
20
21
  import { AsyncEventQueue } from './async-queue.js';
21
22
  /** Converts a small plugin executor contract into Maka's existing Run event stream. */
22
23
  export class PluginExecutorBackend {
23
24
  kind = 'plugin-executor';
24
25
  sessionId;
25
26
  #cwd;
27
+ #configuration;
26
28
  #instructions;
29
+ #model;
30
+ #thinkingLevel;
27
31
  #binding;
28
32
  #newId;
29
33
  #now;
@@ -32,7 +36,10 @@ export class PluginExecutorBackend {
32
36
  constructor(input) {
33
37
  this.sessionId = input.sessionId;
34
38
  this.#cwd = input.cwd;
39
+ this.#configuration = input.configuration;
35
40
  this.#instructions = input.instructions;
41
+ this.#model = input.model;
42
+ this.#thinkingLevel = input.thinkingLevel;
36
43
  this.#binding = input.binding;
37
44
  this.#newId = input.newId ?? randomUUID;
38
45
  this.#now = input.now ?? Date.now;
@@ -82,6 +89,7 @@ export class PluginExecutorBackend {
82
89
  const turnId = input.turnId;
83
90
  let thinkingText = '';
84
91
  const toolUseIds = new Map();
92
+ const toolOutputSequences = new Map();
85
93
  let result;
86
94
  let failure;
87
95
  let failed = false;
@@ -93,6 +101,9 @@ export class PluginExecutorBackend {
93
101
  conversationKey: this.sessionId,
94
102
  text: input.text,
95
103
  cwd: this.#cwd,
104
+ ...(this.#model ? { model: this.#model } : {}),
105
+ reasoningEffort: this.#thinkingLevel ?? null,
106
+ ...(this.#configuration ? { configuration: this.#configuration } : {}),
96
107
  ...(this.#instructions ? { instructions: this.#instructions } : {}),
97
108
  ...(input.attachments ? { attachments: input.attachments } : {}),
98
109
  ...(input.directoryReferences ? { directoryReferences: input.directoryReferences } : {}),
@@ -102,8 +113,9 @@ export class PluginExecutorBackend {
102
113
  onEvent: (event) => {
103
114
  if (event.type === 'thinking_delta')
104
115
  thinkingText += event.text;
105
- this.#publishOutputEvent(turnId, messageId, event, toolUseIds, queue);
116
+ this.#publishOutputEvent(turnId, messageId, event, toolUseIds, toolOutputSequences, queue);
106
117
  },
118
+ onPermissionRequest: (request) => this.#requestPermission(input, request, signal, toolUseIds, queue),
107
119
  });
108
120
  }
109
121
  catch (error) {
@@ -124,6 +136,81 @@ export class PluginExecutorBackend {
124
136
  }
125
137
  this.#publishResult(turnId, messageId, result, queue);
126
138
  }
139
+ async #requestPermission(input, request, signal, toolUseIds, queue) {
140
+ const hosted = input.hostedInteraction;
141
+ if (!hosted || signal.aborted)
142
+ return { outcome: 'cancelled' };
143
+ const requestId = this.#newId();
144
+ const event = {
145
+ type: 'form_request',
146
+ id: this.#newId(),
147
+ turnId: input.turnId,
148
+ ts: this.#now(),
149
+ requestId,
150
+ toolUseId: toolUseIds.get(request.toolCallId) ?? request.toolCallId,
151
+ message: request.title,
152
+ requester: {
153
+ name: this.#binding.identity.displayName,
154
+ source: this.#binding.identity.extensionId,
155
+ },
156
+ fields: [
157
+ {
158
+ kind: 'single_select',
159
+ name: 'optionId',
160
+ label: request.kind === 'question' ? 'Question' : 'Permission',
161
+ required: true,
162
+ options: request.options.map((option) => ({
163
+ value: option.optionId,
164
+ label: option.name,
165
+ })),
166
+ },
167
+ ],
168
+ };
169
+ let settle;
170
+ const answer = new Promise((resolve) => {
171
+ settle = resolve;
172
+ });
173
+ let settled = false;
174
+ const finish = (result) => {
175
+ if (settled)
176
+ return;
177
+ settled = true;
178
+ settle(result);
179
+ };
180
+ const settlement = {
181
+ applyAnswer: async (result) => {
182
+ if (result.action !== 'accept')
183
+ return finish({ outcome: 'cancelled' });
184
+ const selected = result.values.optionId;
185
+ if (typeof selected !== 'string' ||
186
+ !request.options.some((option) => option.optionId === selected)) {
187
+ return finish({ outcome: 'cancelled' });
188
+ }
189
+ finish({ outcome: 'selected', optionId: selected });
190
+ },
191
+ applyClosure: async () => finish({ outcome: 'cancelled' }),
192
+ };
193
+ let admission;
194
+ const onAbort = () => {
195
+ finish({ outcome: 'cancelled' });
196
+ void Promise.resolve().then(async () => {
197
+ await admission?.catch(() => undefined);
198
+ await hosted.withdrawFormRequest(requestId).catch(() => undefined);
199
+ });
200
+ };
201
+ signal.addEventListener('abort', onAbort, { once: true });
202
+ try {
203
+ admission = hosted.admitFormRequest({ request: event, settlement });
204
+ await admission;
205
+ if (signal.aborted)
206
+ return { outcome: 'cancelled' };
207
+ queue.push(event);
208
+ return await answer;
209
+ }
210
+ finally {
211
+ signal.removeEventListener('abort', onAbort);
212
+ }
213
+ }
127
214
  #closeOptionalOutput(turnId, messageId, thinkingText, toolUseIds, queue) {
128
215
  if (thinkingText) {
129
216
  queue.push({
@@ -188,9 +275,10 @@ export class PluginExecutorBackend {
188
275
  turnId,
189
276
  ts: this.#now(),
190
277
  stopReason: 'user_stop',
278
+ ...(result.providerStopReason ? { providerStopReason: result.providerStopReason } : {}),
191
279
  });
192
280
  }
193
- #publishOutputEvent(turnId, messageId, event, toolUseIds, queue) {
281
+ #publishOutputEvent(turnId, messageId, event, toolUseIds, toolOutputSequences, queue) {
194
282
  if (event.type === 'output_delta') {
195
283
  if (!event.text)
196
284
  return;
@@ -233,6 +321,7 @@ export class PluginExecutorBackend {
233
321
  }
234
322
  const toolUseId = this.#newId();
235
323
  toolUseIds.set(event.toolCallId, toolUseId);
324
+ toolOutputSequences.set(event.toolCallId, 0);
236
325
  queue.push({
237
326
  type: 'tool_start',
238
327
  id: this.#newId(),
@@ -251,6 +340,31 @@ export class PluginExecutorBackend {
251
340
  const toolUseId = toolUseIds.get(event.toolCallId);
252
341
  if (!toolUseId)
253
342
  return;
343
+ if (event.type === 'tool_output_delta') {
344
+ if (!event.text)
345
+ return;
346
+ const chunk = redactSecrets(event.text);
347
+ if (!chunk)
348
+ return;
349
+ const now = this.#now();
350
+ const seq = (toolOutputSequences.get(event.toolCallId) ?? 0) + 1;
351
+ toolOutputSequences.set(event.toolCallId, seq);
352
+ queue.push({
353
+ type: 'tool_output_delta',
354
+ id: this.#newId(),
355
+ sessionId: this.sessionId,
356
+ turnId,
357
+ ts: now,
358
+ toolCallId: toolUseId,
359
+ toolUseId,
360
+ seq,
361
+ stream: event.stream ?? 'stdout',
362
+ chunk,
363
+ redacted: chunk !== event.text,
364
+ createdAt: now,
365
+ });
366
+ return;
367
+ }
254
368
  if (event.type === 'tool_progress') {
255
369
  if (!event.text)
256
370
  return;
@@ -265,6 +379,7 @@ export class PluginExecutorBackend {
265
379
  return;
266
380
  }
267
381
  toolUseIds.delete(event.toolCallId);
382
+ toolOutputSequences.delete(event.toolCallId);
268
383
  queue.push({
269
384
  type: 'tool_result',
270
385
  id: this.#newId(),
@@ -273,7 +388,9 @@ export class PluginExecutorBackend {
273
388
  toolUseId,
274
389
  providerExecuted: true,
275
390
  isError: event.isError ?? false,
276
- content: { kind: 'text', text: event.text },
391
+ content: event.content.kind === 'text'
392
+ ? event.content
393
+ : { kind: 'file_diff', paths: [...event.content.paths], diff: event.content.diff },
277
394
  });
278
395
  }
279
396
  #publishFailure(turnId, message, code, recoverable, queue) {
@@ -305,6 +422,8 @@ function cancellationEventReason(result) {
305
422
  return 'redirect';
306
423
  if (result.reason === 'timeout')
307
424
  return 'timeout';
425
+ if (result.reason === 'crash')
426
+ return 'crash';
308
427
  if (result.source === 'executor_retired')
309
428
  return 'crash';
310
429
  return 'user_stop';