maka-agent 0.2.0-dev.43.20260919 → 0.2.0-dev.45.20260921

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 (89) hide show
  1. package/LICENSE +12 -6
  2. package/dist/mcp-capability-provider.js +7 -0
  3. package/dist/runtime-host-service-manager.js +1 -6
  4. package/dist/runtime-host-tui-command.js +1 -1
  5. package/dist/runtime-host-update-command.js +39 -27
  6. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  7. package/node_modules/@maka/core/dist/artifacts.js +1 -0
  8. package/node_modules/@maka/core/dist/chat-model-choice.js +3 -0
  9. package/node_modules/@maka/core/dist/llm-connections.js +3 -21
  10. package/node_modules/@maka/core/dist/model-catalog.js +8 -20
  11. package/node_modules/@maka/core/dist/model-metadata.generated.js +304 -304
  12. package/node_modules/@maka/core/dist/model-metadata.js +1 -2
  13. package/node_modules/@maka/core/dist/model-thinking.js +16 -1
  14. package/node_modules/@maka/core/dist/models-dev-projection.js +0 -1
  15. package/node_modules/@maka/core/dist/provider-auth.js +1 -7
  16. package/node_modules/@maka/core/dist/provider-registry.js +20 -58
  17. package/node_modules/@maka/core/dist/recall.js +5 -1
  18. package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +7 -0
  19. package/node_modules/@maka/core/dist/runtime-policy/model-catalog-entry-codec.js +14 -1
  20. package/node_modules/@maka/core/dist/session-start-mode.js +2 -5
  21. package/node_modules/@maka/core/dist/session.js +0 -1
  22. package/node_modules/@maka/core/dist/settings.js +19 -3
  23. package/node_modules/@maka/core/dist/tool-quiet-preview.js +2 -16
  24. package/node_modules/@maka/core/dist/transcript-search.js +46 -0
  25. package/node_modules/@maka/core/package.json +1 -4
  26. package/node_modules/@maka/mcp/dist/index.js +22 -0
  27. package/node_modules/@maka/mcp/dist/oauth.js +4 -2
  28. package/node_modules/@maka/mcp/dist/tool-progress.js +45 -0
  29. package/node_modules/@maka/runtime/dist/model-factory.js +0 -16
  30. package/node_modules/@maka/runtime/dist/model-fetcher.js +1 -4
  31. package/node_modules/@maka/runtime/dist/model-runtime.js +0 -8
  32. package/node_modules/@maka/runtime/dist/opencode-session-header.js +2 -3
  33. package/node_modules/@maka/runtime/dist/recall-tools.js +4 -0
  34. package/node_modules/@maka/runtime/dist/session-manager.js +5 -57
  35. package/node_modules/@maka/runtime/dist/subagent-tools.js +69 -11
  36. package/node_modules/@maka/runtime/dist/test-connection.js +4 -114
  37. package/node_modules/@maka/runtime/package.json +0 -1
  38. package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +44 -9
  39. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +25 -18
  40. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +3 -1
  41. package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
  42. package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +33 -3
  43. package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +0 -108
  44. package/node_modules/@maka/runtime-host/dist/protocol/index.js +12 -1
  45. package/node_modules/@maka/runtime-host/dist/protocol/message.js +10 -0
  46. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +4 -6
  47. package/node_modules/@maka/runtime-host/dist/protocol/recall.js +270 -0
  48. package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +1 -1
  49. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -7
  50. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +0 -33
  51. package/node_modules/@maka/runtime-host/dist/protocol/usage-screen.js +12 -4
  52. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +6 -0
  53. package/node_modules/@maka/runtime-host/dist/server/bootstrap-runtime-policy.js +31 -104
  54. package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +1 -57
  55. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +11 -35
  56. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +3 -20
  57. package/node_modules/@maka/runtime-host/dist/server/interactive-turn-coordinator.js +1 -16
  58. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +46 -28
  59. package/node_modules/@maka/runtime-host/dist/server/recall-coordinator.js +117 -0
  60. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +5 -16
  61. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +21 -12
  62. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +0 -4
  63. package/node_modules/@maka/runtime-host/dist/server/session-turn-access-request-coordinator.js +7 -17
  64. package/node_modules/@maka/storage/dist/artifact-metadata-codec.js +4 -3
  65. package/node_modules/@maka/storage/dist/artifact-store.js +1 -11
  66. package/node_modules/@maka/storage/dist/process-lifetime-file-update-lock.js +0 -41
  67. package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +0 -62
  68. package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +0 -31
  69. package/node_modules/@maka/storage/dist/runtime-policy-stores.js +0 -3
  70. package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +29 -5
  71. package/node_modules/@maka/storage/dist/sqlite-workflow-schema.js +2 -0
  72. package/node_modules/@maka/storage/dist/storage-writer-composition.js +0 -3
  73. package/node_modules/@maka/storage/dist/telemetry-file-schema.js +5 -3
  74. package/node_modules/@maka/storage/dist/usage-screen.js +5 -6
  75. package/node_modules/@maka/storage/dist/usage-stores.js +14 -4
  76. package/node_modules/@maka/storage/package.json +0 -2
  77. package/package.json +1 -1
  78. package/node_modules/@maka/core/dist/connection-usage.js +0 -27
  79. package/node_modules/@maka/core/dist/deep-research-client-progress.js +0 -132
  80. package/node_modules/@maka/core/dist/deep-research-run.js +0 -625
  81. package/node_modules/@maka/core/dist/deep-research.js +0 -208
  82. package/node_modules/@maka/core/dist/thread-search.js +0 -498
  83. package/node_modules/@maka/runtime/dist/commandcode-cli-language-model.js +0 -837
  84. package/node_modules/@maka/runtime/dist/connection-usage.js +0 -232
  85. package/node_modules/@maka/runtime/dist/deep-research-tools.js +0 -732
  86. package/node_modules/@maka/runtime-host/dist/protocol/deep-research.js +0 -233
  87. package/node_modules/@maka/runtime-host/dist/server/deep-research-coordinator.js +0 -116
  88. package/node_modules/@maka/storage/dist/deep-research-authority.js +0 -129
  89. package/node_modules/@maka/storage/dist/deep-research-store.js +0 -343
@@ -17,11 +17,10 @@
17
17
  * under the License.
18
18
  */
19
19
  export const OPENCODE_SESSION_HEADER = 'x-opencode-session';
20
- /** Adds OpenCode Go/Free session identity without overriding an explicit header. */
20
+ /** Adds OpenCode Go session identity without overriding an explicit header. */
21
21
  export function withOpenCodeSessionHeader(providerType, sessionId, headers) {
22
22
  const normalizedSessionId = sessionId?.trim();
23
- if ((providerType !== 'opencode-go' && providerType !== 'opencode-free') ||
24
- !normalizedSessionId) {
23
+ if (providerType !== 'opencode-go' || !normalizedSessionId) {
25
24
  return headers;
26
25
  }
27
26
  if (Object.keys(headers ?? {}).some((name) => name.toLowerCase() === OPENCODE_SESSION_HEADER)) {
@@ -230,6 +230,10 @@ function projectPassage(passage, activeSessionId) {
230
230
  title: passage.sessionTitle,
231
231
  ...(passage.turnId ? { turn_id: passage.turnId } : {}),
232
232
  anchor_message_id: passage.anchorMessageId,
233
+ // The anchor's index in its Session transcript. Carried for the same
234
+ // reason a UI needs it: a caller that wants to point at the passage in a
235
+ // transcript scrolls by sequence, and this spares it a second lookup.
236
+ sequence: passage.sequence,
233
237
  is_current_session: passage.sessionId === activeSessionId,
234
238
  ...(passage.lastMessageAt !== undefined ? { last_message_at: passage.lastMessageAt } : {}),
235
239
  matched_terms: passage.matchedTerms,
@@ -20,12 +20,11 @@ import { countRecallSearchableMessages, listRecallCandidateSessions, } from './r
20
20
  import { createHash } from 'node:crypto';
21
21
  import { isDeepStrictEqual } from 'node:util';
22
22
  import { setTimeout as delay } from 'node:timers/promises';
23
- import { messageContentsEqual, normalizeMessageContent } from '@maka/core/events';
23
+ import { messageContentsEqual } from '@maka/core/events';
24
24
  import { isCanonicalReadOnlyPermissionProfile } from '@maka/core/permission-profile';
25
25
  import { DEFAULT_TOOL_MODE } from '@maka/core/tool-mode';
26
26
  import { PLAN_USER_ABANDON_REASON, PLAN_USER_CANCEL_REASON, PlanConflictError, activePlanExecution, } from '@maka/core/plan';
27
27
  import { DEFAULT_SESSION_NAME } from '@maka/core/session-name';
28
- import { DEEP_RESEARCH_SESSION_LABEL, isDeepResearchSession } from '@maka/core/deep-research';
29
28
  import { SUBAGENT_SESSION_RUNTIME_SCHEMA_VERSION, SUBAGENT_SESSION_SPAWN_SCHEMA_VERSION, childSessionsForParent, subagentSessionRuntimeSummary, } from '@maka/core/session';
30
29
  import { decodeAgentGraphIntentClaim } from '@maka/core/agent-graph-control';
31
30
  import { executionBoundaryContains } from '@maka/core/sandbox-boundary';
@@ -97,14 +96,6 @@ export class SessionConfigurationRevisionConflictError extends Error {
97
96
  this.actualRevision = actualRevision;
98
97
  }
99
98
  }
100
- export class RuntimeRegenerateTurnError extends Error {
101
- code;
102
- name = 'RuntimeRegenerateTurnError';
103
- constructor(code, message) {
104
- super(message);
105
- this.code = code;
106
- }
107
- }
108
99
  export class BackendRegistry {
109
100
  registrations = new Map();
110
101
  register(kind, registration) {
@@ -355,11 +346,7 @@ export class SessionManager {
355
346
  throw new SessionConfigurationTransitionError('session_busy', 'Session has a pending Interaction');
356
347
  }
357
348
  await this.assertCollaborationTransition(current.header, input.configuration.collaborationMode);
358
- const leavingDeepResearch = isDeepResearchSession(current.header.labels) &&
359
- input.configuration.permissionMode !== 'explore';
360
- const labels = leavingDeepResearch
361
- ? current.header.labels.filter((label) => label !== DEEP_RESEARCH_SESSION_LABEL)
362
- : current.header.labels;
349
+ const labels = current.header.labels;
363
350
  return () => store.updateSessionConfiguration(sessionId, {
364
351
  expectedVersion: input.expectedRevision,
365
352
  configuration: {
@@ -713,15 +700,11 @@ export class SessionManager {
713
700
  async setPermissionModeWithLegacyStore(sessionId, mode) {
714
701
  const previous = await this.deps.store.readHeader(sessionId);
715
702
  const boundary = await this.deps.store.readExecutionBoundary(sessionId);
716
- const leavingDeepResearch = isDeepResearchSession(previous.labels) && mode !== 'explore';
717
703
  if (previous.permissionMode === mode &&
718
- executionBoundaryMatchesPermissionMode(boundary, mode) &&
719
- !leavingDeepResearch) {
704
+ executionBoundaryMatchesPermissionMode(boundary, mode)) {
720
705
  return headerToSummary(previous);
721
706
  }
722
- const labels = leavingDeepResearch
723
- ? previous.labels.filter((label) => label !== DEEP_RESEARCH_SESSION_LABEL)
724
- : previous.labels;
707
+ const labels = previous.labels;
725
708
  const kind = mode === 'bypass' ? 'bypass' : 'managed';
726
709
  await this.commitExecutionBoundaryTransition(sessionId, boundary, mode, async () => {
727
710
  const current = await this.deps.store.readHeader(sessionId);
@@ -1463,7 +1446,7 @@ export class SessionManager {
1463
1446
  }
1464
1447
  const resolvedPreset = await this.deps.subagentCatalog.resolve(input.subagentId);
1465
1448
  if (resolvedPreset.profile !== input.agentProfile) {
1466
- throw new Error(`Subagent preset "${input.subagentId}" profile changed during spawn`);
1449
+ throw new Error(`Subagent preset "${input.subagentId}" profile changed during spawn. Retry the same agent_spawn call.`);
1467
1450
  }
1468
1451
  return { ...input, resolvedPreset };
1469
1452
  }
@@ -2752,41 +2735,6 @@ export class SessionManager {
2752
2735
  ? authority.stopRoot(identity, input)
2753
2736
  : this.runtimeKernel.stopSession(identity.sessionId, input);
2754
2737
  }
2755
- async *regenerateTurn(sessionId, input) {
2756
- const execution = this.runtimeKernel.claimExecution(sessionId);
2757
- try {
2758
- const source = await this.prepareRegenerateTurn(sessionId, input.sourceTurnId);
2759
- yield* this.sendMessage(sessionId, {
2760
- turnId: input.turnId ?? this.deps.newId(),
2761
- ...source.content,
2762
- parentTurnId: source.sourceTurnId,
2763
- regeneratedFromTurnId: source.sourceTurnId,
2764
- }, { execution });
2765
- }
2766
- finally {
2767
- execution.release();
2768
- }
2769
- }
2770
- async prepareRegenerateTurn(sessionId, sourceTurnId) {
2771
- const view = await this.getSessionView(sessionId);
2772
- const source = view.turns.find((candidate) => candidate.turnId === sourceTurnId);
2773
- if (!source) {
2774
- throw new RuntimeRegenerateTurnError('not_found', `Cannot regenerate unknown Turn ${sourceTurnId}`);
2775
- }
2776
- if (source.status !== 'failed' &&
2777
- source.status !== 'aborted' &&
2778
- source.status !== 'completed') {
2779
- throw new RuntimeRegenerateTurnError('operation_conflict', `Cannot regenerate Turn ${sourceTurnId} while it is ${source.status}`);
2780
- }
2781
- const user = view.messages.find((message) => message.type === 'user' && message.turnId === sourceTurnId);
2782
- if (!user) {
2783
- throw new RuntimeRegenerateTurnError('operation_conflict', `Turn ${sourceTurnId} has no UserMessage`);
2784
- }
2785
- return {
2786
- sourceTurnId,
2787
- content: normalizeMessageContent(user),
2788
- };
2789
- }
2790
2738
  /** Canonical, repaired source view for a Host-owned cross-Session copy. */
2791
2739
  async readConversationCopySnapshot(sessionId) {
2792
2740
  return this.getSessionView(sessionId);
@@ -46,6 +46,7 @@ const AGENT_LIST_PAGE_SIZE = 8;
46
46
  const AGENT_LIST_MAX_RESPONSE_CHARS = 7_000;
47
47
  const AGENT_LIST_DESCRIPTION_MAX_CHARS = 240;
48
48
  const AGENT_LIST_MODEL_MAX_CHARS = 160;
49
+ const CHILD_EXECUTOR_SELECTION_GUIDANCE = 'Use executor_mode=inherit for the preset or inherited execution route; executor_id is then ignored. Use executor_mode=plugin only for an explicitly selected registered plugin executor. Without executor_mode, omit executor_id to inherit; "default" is not a default selector.';
49
50
  /**
50
51
  * Which schema fields each `agent_output` locator needs. A rejection that only
51
52
  * says "its matching identity fields" leaves the model guessing which of the
@@ -76,22 +77,34 @@ export function buildSubagentSpawnTool(deps = {}) {
76
77
  return {
77
78
  name: AGENT_SPAWN_TOOL_NAME,
78
79
  displayName: 'Agent',
79
- description: 'Run one bounded foreground child task. Prefer agent_list, then select the user-approved subagent_id whose description fits the task; profile is retained for legacy callers. If both selectors are present, subagent_id wins and profile is ignored.',
80
+ description: "Run one bounded foreground child task. Call agent_list and copy an available choice's spawn_args, then add task. target_kind=profile uses profile; target_kind=preset uses subagent_id. The unused selector is ignored. Empty presets does not disable built-in profiles. Without target_kind, subagent_id takes precedence for legacy callers. Independent child tasks may be called together in one parallel batch, including corrected retries after selector errors. " +
81
+ CHILD_EXECUTOR_SELECTION_GUIDANCE,
80
82
  parameters: z.preprocess(cleanSubagentSpawnInput, z
81
83
  .object({
82
- profile: z.enum(profiles).optional().describe('Legacy child capability profile.'),
84
+ target_kind: z
85
+ .enum(['profile', 'preset'])
86
+ .optional()
87
+ .describe('Choose profile for legacy_profiles or preset for presets. Only the selected identity field is used; the other is ignored even if populated.'),
88
+ profile: z
89
+ .enum(profiles)
90
+ .optional()
91
+ .describe('Built-in child capability: copy legacy_profiles[].profile from agent_list.'),
83
92
  subagent_id: z
84
93
  .string()
85
94
  .min(1)
86
95
  .max(128)
87
96
  .refine(isSafeSubagentPresetId)
88
97
  .optional()
89
- .describe('User-approved subagent preset id from agent_list.'),
98
+ .describe('For target_kind=preset, copy presets[].subagent_id from agent_list. Ignored for target_kind=profile.'),
99
+ executor_mode: z
100
+ .enum(['inherit', 'plugin'])
101
+ .optional()
102
+ .describe(CHILD_EXECUTOR_SELECTION_GUIDANCE),
90
103
  executor_id: z
91
104
  .string()
92
105
  .refine(isExecutorId)
93
106
  .optional()
94
- .describe('Plugin executor id for this child task.'),
107
+ .describe(CHILD_EXECUTOR_SELECTION_GUIDANCE),
95
108
  task: z
96
109
  .string()
97
110
  .min(1)
@@ -108,12 +121,30 @@ export function buildSubagentSpawnTool(deps = {}) {
108
121
  })
109
122
  .strip()
110
123
  .superRefine((input, ctx) => {
111
- if (!input.profile && !input.subagent_id) {
124
+ if (input.executor_mode === 'plugin' && !input.executor_id) {
112
125
  ctx.addIssue({
113
126
  code: z.ZodIssueCode.custom,
114
- message: 'No child selector was provided. Call agent_list and pass a returned subagent_id to agent_spawn, ' +
115
- `or pass one legacy profile: ${profiles.join(', ')}.`,
127
+ path: ['executor_id'],
128
+ message: 'executor_mode=plugin requires a registered executor_id. Use executor_mode=inherit for normal child execution.',
116
129
  });
130
+ }
131
+ if (!input.profile && !input.subagent_id) {
132
+ if (input.target_kind) {
133
+ const selector = input.target_kind === 'profile' ? 'profile' : 'subagent_id';
134
+ const catalogSection = input.target_kind === 'profile' ? 'legacy_profiles' : 'presets';
135
+ ctx.addIssue({
136
+ code: z.ZodIssueCode.custom,
137
+ path: [selector],
138
+ message: `target_kind=${input.target_kind} requires ${selector}. Call agent_list and copy an available ${catalogSection} choice's spawn_args, then add task.`,
139
+ });
140
+ }
141
+ else {
142
+ ctx.addIssue({
143
+ code: z.ZodIssueCode.custom,
144
+ message: 'No child selector was provided. Call agent_list and pass a returned subagent_id to agent_spawn, ' +
145
+ `or pass one legacy profile: ${profiles.join(', ')}.`,
146
+ });
147
+ }
117
148
  return;
118
149
  }
119
150
  if (input.subagent_id)
@@ -185,8 +216,12 @@ function cleanSubagentSpawnInput(input) {
185
216
  if (!input || typeof input !== 'object' || Array.isArray(input))
186
217
  return input;
187
218
  const cleaned = { ...input };
188
- if (cleaned.subagent_id !== undefined)
219
+ if (cleaned.target_kind === 'profile')
220
+ delete cleaned.subagent_id;
221
+ else if (cleaned.target_kind === 'preset' || cleaned.subagent_id !== undefined)
189
222
  delete cleaned.profile;
223
+ if (cleaned.executor_mode === 'inherit')
224
+ delete cleaned.executor_id;
190
225
  return cleaned;
191
226
  }
192
227
  async function resolvePresetDefinition(subagentId, ctx, definitions) {
@@ -205,8 +240,13 @@ async function resolvePresetDefinition(subagentId, ctx, definitions) {
205
240
  !Array.isArray(candidate) &&
206
241
  candidate.id === subagentId &&
207
242
  typeof candidate.profile === 'string');
208
- if (!preset)
209
- throw new Error(`Unknown subagent_id "${subagentId}". Call agent_list first.`);
243
+ if (!preset) {
244
+ const legacy = definitions.find((definition) => definition.profile === subagentId || definition.id === subagentId);
245
+ const recovery = legacy
246
+ ? `"${subagentId}" identifies a built-in agent, not a configured preset. Use ${JSON.stringify({ target_kind: 'profile', profile: legacy.profile, executor_mode: 'inherit' })}, keeping your task. The unused subagent_id and executor_id fields are ignored in these modes.`
247
+ : "Call agent_list and copy an available choice's spawn_args, then add task. presets use subagent_id; legacy_profiles use profile.";
248
+ throw new Error(`Unknown subagent_id "${subagentId}". No child was started. ${recovery} ${CHILD_EXECUTOR_SELECTION_GUIDANCE}`);
249
+ }
210
250
  if (preset.availability?.status !== 'available') {
211
251
  throw new Error(`Subagent preset "${subagentId}" is unavailable.`);
212
252
  }
@@ -249,7 +289,7 @@ export function buildSubagentListTool() {
249
289
  return {
250
290
  name: AGENT_LIST_TOOL_NAME,
251
291
  displayName: 'Agent List',
252
- description: 'List a compact page of subagents to select. The default selection view returns runnable user-approved subagent_id values first, followed by legacy choices with separate agent_id (Graph) and profile (agent_spawn) selectors. Use view=catalog only to diagnose unavailable routes. Child execution history is intentionally excluded; use refs returned by agent_spawn or asynchronous graph work with agent_output.',
292
+ description: "List a compact page of subagents to select. Copy an available choice's spawn_args into agent_spawn and add task. presets use subagent_id; legacy_profiles use profile, including when presets is empty. agent_id is for Graph only. Use view=catalog only to diagnose unavailable routes. Child execution history is intentionally excluded; use refs returned by agent_spawn or asynchronous graph work with agent_output.",
253
293
  parameters: z
254
294
  .object({
255
295
  view: z
@@ -309,6 +349,15 @@ function projectAgentList(catalog, input) {
309
349
  return [
310
350
  {
311
351
  subagent_id: preset.id,
352
+ ...(availability.status === 'available'
353
+ ? {
354
+ spawn_args: {
355
+ target_kind: 'preset',
356
+ subagent_id: preset.id,
357
+ executor_mode: 'inherit',
358
+ },
359
+ }
360
+ : {}),
312
361
  name: boundedCatalogText(preset.name, 128),
313
362
  description: boundedCatalogText(preset.description, AGENT_LIST_DESCRIPTION_MAX_CHARS),
314
363
  profile: preset.profile,
@@ -345,6 +394,15 @@ function projectAgentList(catalog, input) {
345
394
  {
346
395
  agent_id: definition.id,
347
396
  profile: definition.profile,
397
+ ...(availability.status === 'available'
398
+ ? {
399
+ spawn_args: {
400
+ target_kind: 'profile',
401
+ profile: definition.profile,
402
+ executor_mode: 'inherit',
403
+ },
404
+ }
405
+ : {}),
348
406
  name: boundedCatalogText(definition.name, 128),
349
407
  description: boundedCatalogText(definition.description, AGENT_LIST_DESCRIPTION_MAX_CHARS),
350
408
  ...(typeof contract?.workspace === 'string'
@@ -17,12 +17,11 @@
17
17
  * under the License.
18
18
  */
19
19
  import { randomUUID } from 'node:crypto';
20
- import { buildCommandCodeCliRequest, commandCodeCliGenerateUrl, commandCodeCliHeaders, summarizeCommandCodeCliStream, } from './commandcode-cli-language-model.js';
21
20
  import { PROVIDER_REGISTRY, effectiveBaseUrl, providerDefaultsOf, providerFallbackModelIds, connectionModelsEnumerateAccount, connectionEnabledModelIds, } from '@maka/core/llm-connections';
22
21
  import { openResponsesUrl } from './provider-urls.js';
23
22
  import { resolveModelRuntime } from './model-runtime.js';
24
23
  import { fetchGitHubCopilotModels } from './model-fetcher.js';
25
- import { CONNECTION_EFFECT_ERROR_BODY_MAX_BYTES, CONNECTION_EFFECT_JSON_BODY_MAX_BYTES, ConnectionEffectFetchError, fetchForConnectionEffect, } from './connection-effect-fetch.js';
24
+ import { CONNECTION_EFFECT_ERROR_BODY_MAX_BYTES, ConnectionEffectFetchError, fetchForConnectionEffect, } from './connection-effect-fetch.js';
26
25
  import { ConnectionEffectHttpError, ConnectionEffectInvalidResponseError, classifyConnectionEffectStatus, } from './connection-effect-outcome.js';
27
26
  import { withOpenCodeSessionHeader } from './opencode-session-header.js';
28
27
  const CONNECTION_TEST_TIMEOUT_MS = 15_000;
@@ -106,47 +105,15 @@ async function testConnectionStrict(connection, apiKey, model, fetchFn, t0, time
106
105
  if (!defaults) {
107
106
  return { ok: false, errorMessage: `Unknown provider type "${connection.providerType}"` };
108
107
  }
109
- const sessionId = connection.providerType === 'opencode-go' || connection.providerType === 'opencode-free'
110
- ? randomUUID()
111
- : undefined;
108
+ if (defaults.retired)
109
+ return retiredProviderTestResult(connection.providerType);
110
+ const sessionId = connection.providerType === 'opencode-go' ? randomUUID() : undefined;
112
111
  const auth = defaults.authKind;
113
112
  const secret = auth === 'none' ? '' : apiKey;
114
113
  const testModel = resolveConnectionTestModel(connection, model, providerFallbackModelIds(defaults));
115
114
  if (!testModel) {
116
115
  return { ok: false, errorMessage: 'No model to test' };
117
116
  }
118
- if (connection.providerType === 'opencode-free' && !model?.trim()) {
119
- const brokenModelIds = new Set(defaults.brokenModelIds ?? []);
120
- const candidates = [
121
- ...new Set([
122
- ...connectionEnabledModelIds(connection).filter((id) => !brokenModelIds.has(id)),
123
- ...providerFallbackModelIds(defaults),
124
- ]),
125
- ];
126
- if (candidates.length === 0) {
127
- return { ok: false, errorMessage: 'No model to test' };
128
- }
129
- let lastFailure;
130
- for (let index = 0; index < candidates.length; index += 1) {
131
- const candidate = candidates[index];
132
- const remainingMs = timeoutMs - (Date.now() - t0);
133
- if (remainingMs <= 0) {
134
- return connectionTestFailure(new ConnectionEffectFetchError('timeout'), t0);
135
- }
136
- const remainingCandidates = candidates.length - index;
137
- const attemptTimeoutMs = Math.max(1, Math.floor(remainingMs / remainingCandidates));
138
- try {
139
- const result = await testConnectionModel(connection, secret, candidate, fetchFn, t0, attemptTimeoutMs, sessionId);
140
- if (result.ok)
141
- return result;
142
- lastFailure = result;
143
- }
144
- catch (error) {
145
- lastFailure = connectionTestFailure(error, t0, true);
146
- }
147
- }
148
- return lastFailure ?? connectionTestFailure(new ConnectionEffectFetchError('timeout'), t0);
149
- }
150
117
  return await testConnectionModel(connection, secret, testModel, fetchFn, t0, timeoutMs, sessionId);
151
118
  }
152
119
  async function testConnectionModel(connection, secret, testModel, fetchFn, t0, timeoutMs = CONNECTION_TEST_TIMEOUT_MS, sessionId) {
@@ -179,54 +146,8 @@ async function testConnectionModel(connection, secret, testModel, fetchFn, t0, t
179
146
  return await probeGoogle(baseUrl, secret, testModel, t0, adapter.normalizeBaseUrl !== false, fetchFn);
180
147
  case 'cohere':
181
148
  return await probeCohere(baseUrl, secret, testModel, t0, fetchFn);
182
- case 'commandcode-cli':
183
- return await probeCommandCodeCli(baseUrl, secret, testModel, t0, fetchFn, requestHeaders);
184
149
  }
185
150
  }
186
- async function probeCommandCodeCli(baseUrl, apiKey, model, t0, fetchFn, requestHeaders) {
187
- const { body } = buildCommandCodeCliRequest({ prompt: [{ role: 'user', content: [{ type: 'text', text: 'Hi' }] }], maxOutputTokens: 16 }, { modelId: model });
188
- const r = await fetchForConnectionEffect(fetchFn, commandCodeCliGenerateUrl(baseUrl), {
189
- method: 'POST',
190
- headers: { ...commandCodeCliHeaders(apiKey, 'maka'), ...(requestHeaders ?? {}) },
191
- body: JSON.stringify(body),
192
- timeoutMs: CONNECTION_TEST_TIMEOUT_MS,
193
- });
194
- if (!r.ok)
195
- return httpFailure(r, t0);
196
- // HTTP 200 is only the handshake on this wire. The generation adapter fails
197
- // the send on an in-band `error` event and on a stream that ends without a
198
- // `finish`, so a probe that stopped at the status would store a connection
199
- // as verified whose very next send is rejected.
200
- const outcome = summarizeCommandCodeCliStream(await r.readText(CONNECTION_EFFECT_JSON_BODY_MAX_BYTES));
201
- const latencyMs = Date.now() - t0;
202
- if (outcome.error) {
203
- const { message, statusCode } = outcome.error;
204
- return {
205
- ok: false,
206
- latencyMs,
207
- errorMessage: message.slice(0, 200),
208
- ...(statusCode === undefined ? {} : { statusCode }),
209
- errorClass: commandCodeCliStreamErrorClass(statusCode),
210
- };
211
- }
212
- if (!outcome.finished) {
213
- return {
214
- ok: false,
215
- latencyMs,
216
- errorMessage: 'The Command Code GO stream ended before the turn finished',
217
- errorClass: 'network',
218
- };
219
- }
220
- return { ok: true, latencyMs, modelTested: model };
221
- }
222
- function commandCodeCliStreamErrorClass(statusCode) {
223
- if (statusCode === 401 || statusCode === 403)
224
- return 'auth';
225
- if (statusCode === 429 || (statusCode !== undefined && statusCode >= 500)) {
226
- return 'provider_unavailable';
227
- }
228
- return 'unknown';
229
- }
230
151
  async function probeGitHubCopilot(baseUrl, apiKey, model, t0, fetchFn) {
231
152
  const models = await fetchGitHubCopilotModels(baseUrl, apiKey, fetchFn);
232
153
  if (!models.some(({ id }) => id === model)) {
@@ -332,40 +253,9 @@ async function probeOpenAI(connection, baseUrl, apiKey, model, t0, fetchFn, time
332
253
  });
333
254
  if (!r.ok)
334
255
  return httpFailure(r, t0);
335
- if (connection.providerType === 'opencode-free') {
336
- const body = await r.readJson();
337
- if (!isOpenAIChatCompletion(body)) {
338
- return {
339
- ok: false,
340
- errorMessage: 'OpenCode Free returned no valid chat completion',
341
- errorClass: 'provider_unavailable',
342
- latencyMs: Date.now() - t0,
343
- modelTested: model,
344
- };
345
- }
346
- return { ok: true, latencyMs: Date.now() - t0, modelTested: model };
347
- }
348
256
  await r.cancel();
349
257
  return { ok: true, latencyMs: Date.now() - t0, modelTested: model };
350
258
  }
351
- function isOpenAIChatCompletion(value) {
352
- if (!value || typeof value !== 'object')
353
- return false;
354
- const choices = value.choices;
355
- return (Array.isArray(choices) &&
356
- choices.some((choice) => {
357
- if (!choice || typeof choice !== 'object')
358
- return false;
359
- const message = choice.message;
360
- if (!message || typeof message !== 'object')
361
- return false;
362
- const completion = message;
363
- return (typeof completion.content === 'string' ||
364
- typeof completion.reasoning === 'string' ||
365
- typeof completion.reasoning_content === 'string' ||
366
- (Array.isArray(completion.tool_calls) && completion.tool_calls.length > 0));
367
- }));
368
- }
369
259
  async function probeGoogle(baseUrl, apiKey, model, t0, normalizeBaseUrl, fetchFn) {
370
260
  const url = `${stripTrailing(baseUrl)}/models/${encodeURIComponent(model)}:generateContent${normalizeBaseUrl ? `?key=${encodeURIComponent(apiKey)}` : ''}`;
371
261
  const r = await fetchForConnectionEffect(fetchFn, url, {
@@ -13,7 +13,6 @@
13
13
  "./shell-tools": "./dist/shell-tools.js",
14
14
  "./shell-run-manager": "./dist/shell-run-manager.js",
15
15
  "./background-task-health-tool": "./dist/background-task-health-tool.js",
16
- "./deep-research-tools": "./dist/deep-research-tools.js",
17
16
  "./durable-tool-result-projection": "./dist/durable-tool-result-projection.js",
18
17
  "./tool-artifacts": "./dist/tool-artifacts.js",
19
18
  "./model-factory": "./dist/model-factory.js",
@@ -17,7 +17,7 @@
17
17
  * under the License.
18
18
  */
19
19
  import { randomUUID } from 'node:crypto';
20
- import { CLIENT_CAPABILITY_MAX_RESULT_BYTES, CLIENT_CAPABILITY_RESULT_CHUNK_MAX_BYTES, decodeClientCapabilityClientFrame, decodeClientCapabilityReplaceInput, decodeClientCapabilityResult, } from '../protocol/index.js';
20
+ import { CLIENT_CAPABILITY_MAX_PROGRESS_TOTAL, CLIENT_CAPABILITY_MAX_RESULT_BYTES, CLIENT_CAPABILITY_RESULT_CHUNK_MAX_BYTES, decodeClientCapabilityClientFrame, decodeClientCapabilityReplaceInput, decodeClientCapabilityResult, } from '../protocol/index.js';
21
21
  /** Client-owned registration, reverse invocation, and teardown lifecycle. */
22
22
  export class ClientCapabilityChannel {
23
23
  #options;
@@ -247,6 +247,40 @@ export class ClientCapabilityChannel {
247
247
  });
248
248
  return accepting;
249
249
  };
250
+ let pendingProgress;
251
+ let progressWriting = false;
252
+ let progressWrite = Promise.resolve();
253
+ const scheduleProgressWrite = () => {
254
+ if (progressWriting)
255
+ return;
256
+ progressWriting = true;
257
+ progressWrite = (async () => {
258
+ while (pendingProgress) {
259
+ const next = pendingProgress;
260
+ pendingProgress = undefined;
261
+ try {
262
+ await this.#options.write({
263
+ kind: 'client.capability.progress',
264
+ invocationId,
265
+ current: next.current,
266
+ total: next.total,
267
+ });
268
+ }
269
+ catch {
270
+ // Progress is advisory: a failed write must not fail the invocation.
271
+ return;
272
+ }
273
+ }
274
+ })().finally(() => {
275
+ progressWriting = false;
276
+ });
277
+ };
278
+ const flushProgress = async () => {
279
+ do {
280
+ scheduleProgressWrite();
281
+ await progressWrite;
282
+ } while (pendingProgress);
283
+ };
250
284
  try {
251
285
  const progress = (current, total) => {
252
286
  if (!accepted ||
@@ -255,17 +289,17 @@ export class ClientCapabilityChannel {
255
289
  !Number.isInteger(total) ||
256
290
  current < 0 ||
257
291
  total < 1 ||
292
+ total > CLIENT_CAPABILITY_MAX_PROGRESS_TOTAL ||
258
293
  current > total) {
259
294
  return;
260
295
  }
261
- void this.#options
262
- .write({
263
- kind: 'client.capability.progress',
264
- invocationId,
265
- current,
266
- total,
267
- })
268
- .catch((error) => this.#options.onFailure(asError(error)));
296
+ const previous = invocation.progress;
297
+ if (previous && (previous.total !== total || current <= previous.current)) {
298
+ return;
299
+ }
300
+ invocation.progress = { current, total };
301
+ pendingProgress = { current, total };
302
+ scheduleProgressWrite();
269
303
  };
270
304
  const requestInteraction = async (request) => {
271
305
  if (!accepted) {
@@ -315,6 +349,7 @@ export class ClientCapabilityChannel {
315
349
  throw new Error('Client Capability provider returned with a pending interaction');
316
350
  }
317
351
  await accept({ kind: 'none' });
352
+ await flushProgress();
318
353
  await this.#sendResult(invocationId, result, invocation);
319
354
  }
320
355
  catch (error) {
@@ -186,6 +186,25 @@ const COPY = {
186
186
  /** Shared consequence-oriented copy, not a second lifecycle policy. */
187
187
  export function formatHostHandoff(view, locale) {
188
188
  const copy = COPY[locale];
189
+ const labels = {
190
+ cancel: copy.labels.cancel,
191
+ retry: view.manualRecheck ? copy.labels.recheck : copy.labels.retry,
192
+ replace: copy.labels.replace,
193
+ interrupt: view.manualRecheck
194
+ ? copy.labels.interruptRecheck
195
+ : view.operation === 'repair'
196
+ ? copy.labels.interruptRepair
197
+ : copy.labels.interrupt,
198
+ };
199
+ const actions = view.actions.map((action) => ({ action, label: labels[action] }));
200
+ if (view.state === 'progress') {
201
+ return {
202
+ title: copy.progressTitle,
203
+ description: copy.phases[view.phase],
204
+ detail: copy.progressDetail,
205
+ actions,
206
+ };
207
+ }
189
208
  let title = copy.titles[view.reason];
190
209
  let description = copy.descriptions(view.target.name)[view.reason];
191
210
  if (view.recoveryBlocker && view.reason === 'operator_required') {
@@ -215,24 +234,12 @@ export function formatHostHandoff(view, locale) {
215
234
  ? copy.waiting.natural
216
235
  : copy.waiting.blocked;
217
236
  const repairNotice = view.operation === 'repair' ? copy.repairNotice : '';
218
- const labels = {
219
- cancel: copy.labels.cancel,
220
- retry: view.manualRecheck ? copy.labels.recheck : copy.labels.retry,
221
- replace: copy.labels.replace,
222
- interrupt: view.manualRecheck
223
- ? copy.labels.interruptRecheck
224
- : view.operation === 'repair'
225
- ? copy.labels.interruptRepair
226
- : copy.labels.interrupt,
227
- };
228
237
  return {
229
- title: view.state === 'progress' ? copy.progressTitle : title,
230
- description: view.state === 'progress' ? copy.phases[view.phase ?? 'checking'] : description,
231
- detail: view.state === 'progress'
232
- ? copy.progressDetail
233
- : [packageChange, facts, backgroundFacts, waiting, repairNotice, view.operatorStep]
234
- .filter(Boolean)
235
- .join('\n'),
236
- actions: view.actions.map((action) => ({ action, label: labels[action] })),
238
+ title,
239
+ description,
240
+ detail: [packageChange, facts, backgroundFacts, waiting, repairNotice, view.operatorStep]
241
+ .filter(Boolean)
242
+ .join('\n'),
243
+ actions,
237
244
  };
238
245
  }
@@ -172,11 +172,13 @@ export async function runHostHandoff(input) {
172
172
  ? AbortSignal.any([input.signal, attemptAbort.signal])
173
173
  : attemptAbort.signal;
174
174
  const progress = (phase) => publish({
175
- ...current,
176
175
  revision: randomUUID(),
176
+ target: current.target,
177
177
  state: 'progress',
178
178
  phase: cooperative && !interrupt && phase === 'retiring' ? 'pausing' : phase,
179
179
  actions: cancelled ? [] : ['cancel'],
180
+ defaultAction: 'cancel',
181
+ mayExitNaturally: false,
180
182
  });
181
183
  if (input.openSurface)
182
184
  surface ??= input.openSurface(submit);
@@ -30,7 +30,7 @@ export { RuntimeHostSshOperatorActivationError, activateRuntimeHostSshOperator,
30
30
  export { normalizeRuntimeHostSshDestination, openRuntimeHostSshTunnel, } from './ssh-tunnel.js';
31
31
  export { RuntimeHostPermanentReconnectError, startRuntimeHostReconnectLifecycle, } from './reconnect-lifecycle.js';
32
32
  export { RUNTIME_HOST_REMOTE_INCOMPATIBLE_CODE, RuntimeHostRemoteCompatibilityError, } from './remote-compatibility-error.js';
33
- export { RuntimeHostSubscriptionError, } from './session-subscription.js';
33
+ export { RuntimeHostSubscriptionError, SessionRemovedSubscriptionError, subscriptionClosedError, } from './session-subscription.js';
34
34
  export { RuntimeHostStartupError, runtimeHostStartupError, } from './startup-error.js';
35
35
  export { connectRuntimeHostWslEnvironment, listRuntimeHostWslDistributions, normalizeRuntimeHostWslDistribution, resolveSystemRuntimeHostWslExecutable, } from './wsl-environment.js';
36
36
  export { RuntimeHostCatalogReadError, RuntimeHostSessionCatalogRevisionChangedError, readRuntimeHostConnectionCatalog, readRuntimeHostInvocableSkills, readRuntimeHostProjectDetails, readRuntimeHostResources, readRuntimeHostProjects, readRuntimeHostSessionCatalogPage, readRuntimeHostSessions, readRuntimeHostSkillCatalog, } from './catalog-reader.js';