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
@@ -28,6 +28,18 @@ export class RuntimeHostSubscriptionError extends Error {
28
28
  this.name = 'RuntimeHostSubscriptionError';
29
29
  }
30
30
  }
31
+ export class SessionRemovedSubscriptionError extends Error {
32
+ name = 'SessionRemovedSubscriptionError';
33
+ }
34
+ export function subscriptionClosedError(reason) {
35
+ if (reason === 'session_removed') {
36
+ return new SessionRemovedSubscriptionError('Runtime Host Session was removed while it was observed');
37
+ }
38
+ if (reason === 'access_revoked') {
39
+ return new SessionRemovedSubscriptionError('Access to the shared Runtime Host Session was revoked');
40
+ }
41
+ return new RuntimeHostSubscriptionError('slow_consumer', 'Runtime Host Session subscription closed for a slow consumer');
42
+ }
31
43
  function errorMessage(error) {
32
44
  return error instanceof Error ? error.message : String(error);
33
45
  }
@@ -42,6 +54,16 @@ export class ClientSessionSubscription {
42
54
  #readyTask;
43
55
  #readTranscriptPage;
44
56
  #expectedSessionId;
57
+ get transcriptWatermark() {
58
+ return this.#latestTranscriptThroughSequence;
59
+ }
60
+ get deathCause() {
61
+ // #closedReason is recorded at accept()-time, before #terminalError can
62
+ // exist — it is always the authoritative death statement.
63
+ return this.#closedReason === undefined
64
+ ? this.#terminalError
65
+ : subscriptionClosedError(this.#closedReason);
66
+ }
45
67
  #queue = [];
46
68
  #ptyListeners = new Set();
47
69
  #sessionDomainListeners = new Set();
@@ -50,6 +72,7 @@ export class ClientSessionSubscription {
50
72
  #latestProjectionRevision;
51
73
  #waiting;
52
74
  #terminalError;
75
+ #closedReason;
53
76
  #done = false;
54
77
  #doneAfterQueue = false;
55
78
  #closing = false;
@@ -237,7 +260,9 @@ export class ClientSessionSubscription {
237
260
  }
238
261
  }
239
262
  #assertTranscriptReadable() {
240
- if (this.#closing || this.#done || this.#terminalError) {
263
+ if (this.#terminalError)
264
+ throw this.#terminalError;
265
+ if (this.#closing || this.#done) {
241
266
  throw new RuntimeHostSubscriptionError('connection_closed', 'Session subscription closed during transcript loading');
242
267
  }
243
268
  }
@@ -318,9 +343,14 @@ export class ClientSessionSubscription {
318
343
  }
319
344
  }
320
345
  }
321
- this.#offer(frame);
322
- if (frame.kind === 'subscription.closed')
346
+ if (frame.kind === 'subscription.closed') {
347
+ // Record the reason before the offer: a full client queue makes #offer
348
+ // throw 'slow_consumer', which would otherwise discard the real reason
349
+ // the Host gave for closing this subscription.
350
+ this.#closedReason = frame.reason;
323
351
  this.#doneAfterQueue = true;
352
+ }
353
+ this.#offer(frame);
324
354
  }
325
355
  finish() {
326
356
  this.#ptyListeners.clear();
@@ -29,13 +29,6 @@ const EFFECT_ERRORS = [
29
29
  'persistence_failed',
30
30
  'commit_outcome_unknown',
31
31
  ];
32
- /**
33
- * Upper bound on the quota windows one usage report may carry. The producer
34
- * emits three today (5-hour, weekly, monthly); the cap exists because a decoder
35
- * is the boundary that does not trust its producer, matching how every other
36
- * array in this package is bounded before mapping.
37
- */
38
- const CONNECTION_USAGE_MAX_WINDOWS = 16;
39
32
  export const CONNECTION_EFFECT_CHANGED_DOMAINS = [
40
33
  'connection',
41
34
  'credential',
@@ -84,13 +77,6 @@ export const CONNECTION_EFFECT_OPERATION_SPECS = {
84
77
  decodeInput: decodeConnectionTestRunInput,
85
78
  decodeOutput: decodeConnectionTestRunResult,
86
79
  }),
87
- 'connection.usage.read': defineOperation({
88
- mode: 'query',
89
- availability: 'ready',
90
- errors: EFFECT_ERRORS,
91
- decodeInput: decodeConnectionUsageReadInput,
92
- decodeOutput: decodeConnectionUsageReadResult,
93
- }),
94
80
  };
95
81
  export function decodeConnectionOnboardingSaveInput(value) {
96
82
  const input = requireExactRecord(value, 'connection onboarding save input', [
@@ -296,100 +282,6 @@ export function decodeConnectionTestRunResult(value) {
296
282
  }
297
283
  return decodeNonEffectResult(result, 'connection test result');
298
284
  }
299
- export function decodeConnectionUsageReadInput(value) {
300
- const input = requireExactRecord(value, 'connection usage input', ['connectionId']);
301
- return { connectionId: requireEntityId(input.connectionId, 'connectionId') };
302
- }
303
- export function decodeConnectionUsageReadResult(value) {
304
- const result = requireRecord(value, 'connection usage result');
305
- if (result.kind === 'report') {
306
- const report = requireExactRecord(result, 'connection usage report', ['kind', 'report']);
307
- return { kind: 'report', report: decodeUsageReport(report.report) };
308
- }
309
- if (result.kind === 'unavailable') {
310
- const unavailable = requireExactRecord(result, 'connection usage unavailable', [
311
- 'kind',
312
- 'reason',
313
- ]);
314
- if (unavailable.reason !== 'unsupported' &&
315
- unavailable.reason !== 'network' &&
316
- unavailable.reason !== 'no-credential' &&
317
- unavailable.reason !== 'unauthorized') {
318
- throw invalidProtocolFrame('Invalid connection usage unavailable reason');
319
- }
320
- return { kind: 'unavailable', reason: unavailable.reason };
321
- }
322
- if (result.kind === 'rejected') {
323
- const rejected = requireExactRecord(result, 'connection usage result', ['kind', 'reason']);
324
- return { kind: 'rejected', reason: rejectionReason(rejected.reason) };
325
- }
326
- throw invalidProtocolFrame('Invalid connection usage result');
327
- }
328
- function decodeUsageReport(value) {
329
- const report = requireExactRecord(value, 'connection usage report body', [
330
- 'accountLabel',
331
- 'planLabel',
332
- 'stats',
333
- 'windows',
334
- 'periodEnd',
335
- 'partiallyUnauthorized',
336
- 'fetchedAt',
337
- ]);
338
- if (!Array.isArray(report.windows) || report.windows.length > CONNECTION_USAGE_MAX_WINDOWS) {
339
- throw invalidProtocolFrame('Invalid connection usage windows');
340
- }
341
- return {
342
- accountLabel: optionalText(report.accountLabel, 'account label'),
343
- planLabel: optionalText(report.planLabel, 'plan label'),
344
- stats: report.stats === null ? null : decodeUsageStats(report.stats),
345
- windows: report.windows.map(decodeUsageWindow),
346
- periodEnd: optionalMillis(report.periodEnd, 'period end'),
347
- partiallyUnauthorized: requireBoolean(report.partiallyUnauthorized, 'partially unauthorized'),
348
- fetchedAt: requireCount(report.fetchedAt, 'usage fetchedAt'),
349
- };
350
- }
351
- function decodeUsageStats(value) {
352
- const stats = requireExactRecord(value, 'connection usage stats', [
353
- 'requests',
354
- 'failed',
355
- 'successRate',
356
- 'cost',
357
- 'tokensIn',
358
- 'tokensOut',
359
- ]);
360
- return {
361
- requests: optionalCount(stats.requests, 'requests'),
362
- failed: optionalCount(stats.failed, 'failed'),
363
- successRate: optionalAmount(stats.successRate, 'success rate'),
364
- cost: optionalAmount(stats.cost, 'cost'),
365
- tokensIn: optionalCount(stats.tokensIn, 'tokens in'),
366
- tokensOut: optionalCount(stats.tokensOut, 'tokens out'),
367
- };
368
- }
369
- function decodeUsageWindow(value) {
370
- const window = requireExactRecord(value, 'connection usage window', [
371
- 'id',
372
- 'label',
373
- 'used',
374
- 'cap',
375
- 'unit',
376
- 'resetsAt',
377
- 'unlimited',
378
- ]);
379
- const unit = window.unit;
380
- if (unit !== 'credits' && unit !== 'usd' && unit !== 'tokens') {
381
- throw invalidProtocolFrame('Invalid connection usage unit');
382
- }
383
- return {
384
- id: requireUtf8String(window.id, 'usage window id', 64),
385
- label: optionalText(window.label, 'usage window label'),
386
- used: requireAmount(window.used, 'usage window used'),
387
- cap: requireAmount(window.cap, 'usage window cap'),
388
- unit,
389
- resetsAt: optionalMillis(window.resetsAt, 'usage window reset'),
390
- unlimited: window.unlimited === null ? null : requireBoolean(window.unlimited, 'unlimited'),
391
- };
392
- }
393
285
  function optionalText(value, label) {
394
286
  return value === null ? null : requireUtf8String(value, label, 256);
395
287
  }
@@ -62,7 +62,18 @@ export const RUNTIME_HOST_REGISTRATION_SCHEMA_VERSION = 1;
62
62
  export const RUNTIME_HOST_PROTOCOL_VERSION = 0;
63
63
  // Increment when the same protocol version no longer guarantees safe Client-Host
64
64
  // interoperability. Mismatches are rejected before domain commands are admitted.
65
- export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 166;
65
+ export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 172;
66
+ // 172: Usage screen requests and results accept fractional timestamps in the
67
+ // persisted domain. Older peers reject these otherwise valid wire values.
68
+ // 171: Removed the `connection.usage.read` operation along with the Command
69
+ // Code GO provider it served. A peer older than this epoch may still advertise
70
+ // or submit that operation, which this Host no longer answers.
71
+ // 169: The message execution query reports an identity the Host can prove was
72
+ // never admitted as a positive `not_admitted` resolution instead of omitting
73
+ // it, so silence stops meaning both "not admitted" and "cannot say yet".
74
+ // Epoch-168 peers reject the new state as an invalid frame.
75
+ // 167: Removed the turn.regenerate operation. Older peers can no longer
76
+ // safely interoperate because they may submit or advertise that operation.
66
77
  // 166: Connection usage reads add an operation, an accepted availability reason
67
78
  // (`unauthorized`), a report field (`partiallyUnauthorized`), and a bounded
68
79
  // window list. Peers older than this epoch take the added operation for an
@@ -200,6 +200,16 @@ function decodeTurnMessageExecutionQueryResult(value) {
200
200
  state: 'cancelled',
201
201
  };
202
202
  }
203
+ if (resolution.state === 'not_admitted') {
204
+ assertExactKeys(resolution, 'turn.message.execution.query not_admitted resolution', [
205
+ 'messageId',
206
+ 'state',
207
+ ]);
208
+ return {
209
+ messageId: requireEntityId(resolution.messageId, 'messageId'),
210
+ state: 'not_admitted',
211
+ };
212
+ }
203
213
  if (resolution.state === 'owned') {
204
214
  assertExactKeys(resolution, 'turn.message.execution.query owned resolution', [
205
215
  'messageId',
@@ -22,7 +22,6 @@ import { AGENT_GRAPH_OPERATION_SPECS } from './agent-graph.js';
22
22
  import { requireExactRecord, requireId, requireRecord, requireString } from './codec.js';
23
23
  import { CONNECTION_EFFECT_OPERATION_SPECS } from './connection-effects.js';
24
24
  import { CONFIGURATION_OPERATION_SPECS } from './configuration.js';
25
- import { DEEP_RESEARCH_OPERATION_SPECS } from './deep-research.js';
26
25
  import { DAILY_REVIEW_OPERATION_SPECS } from './daily-review.js';
27
26
  import { CONTEXT_OPERATION_SPECS } from './context.js';
28
27
  import { EXECUTION_INSPECT_OPERATION_SPECS } from './execution-inspect.js';
@@ -61,6 +60,7 @@ import { SKILL_CATALOG_OPERATION_SPECS } from './skill-catalog.js';
61
60
  import { TURN_OPERATION_SPECS } from './turn.js';
62
61
  import { USAGE_PRICING_OPERATION_SPECS } from './usage-pricing.js';
63
62
  import { WEB_SEARCH_OPERATION_SPECS } from './web-search.js';
63
+ import { RECALL_OPERATION_SPECS } from './recall.js';
64
64
  import { WORKHUB_COORDINATION_OPERATION_SPECS } from './workhub-coordination.js';
65
65
  export { isHostActivityIdle } from './host-status.js';
66
66
  export { ARTIFACT_CURSOR_MAX_BYTES, ARTIFACT_INGEST_CHUNK_MAX_BYTES, ARTIFACT_INGEST_MIME_TYPE_MAX_BYTES, ARTIFACT_MIME_TYPE_MAX_BYTES, ARTIFACT_NAME_MAX_BYTES, ARTIFACT_PAGE_MAX_ITEMS, ARTIFACT_PREVIEW_MAX_BYTES, ARTIFACT_READ_CHUNK_MAX_BYTES, ARTIFACT_RESULT_MAX_BYTES, ARTIFACT_SUMMARY_MAX_BYTES, decodeArtifactDeleteInput, decodeArtifactDeleteResult, decodeArtifactIngestInput, decodeArtifactIngestResult, decodeArtifactQueryInput, decodeArtifactQueryResult, encodeArtifactDeleteResult, encodeArtifactQueryResult, } from './artifact.js';
@@ -68,7 +68,6 @@ export { TURN_FAILURE_MESSAGE_MAX_BYTES, TURN_MESSAGE_CONTENT_MAX_BYTES, TURN_ME
68
68
  export * from './connection-effects.js';
69
69
  export * from './access-authority.js';
70
70
  export * from './configuration.js';
71
- export * from './deep-research.js';
72
71
  export * from './daily-review.js';
73
72
  export * from './context.js';
74
73
  export * from './agent-graph.js';
@@ -97,8 +96,9 @@ export * from './session-effects.js';
97
96
  export * from './skill-catalog.js';
98
97
  export * from './usage-pricing.js';
99
98
  export * from './web-search.js';
99
+ export * from './recall.js';
100
100
  export * from './workspace.js';
101
- export const HOST_OPERATION_SPECS = composeOperationSpecMaps(HOST_BOOTSTRAP_OPERATION_SPECS, HOST_RESOURCE_OPERATION_SPECS, PEER_MESH_OPERATION_SPECS, HOSTED_EXECUTION_OPERATION_SPECS, ACCESS_AUTHORITY_OPERATION_SPECS, SESSION_COLLABORATION_OPERATION_SPECS, AGENT_GRAPH_OPERATION_SPECS, GOAL_OPERATION_SPECS, TURN_OPERATION_SPECS, CONTEXT_OPERATION_SPECS, CONNECTION_EFFECT_OPERATION_SPECS, DEEP_RESEARCH_OPERATION_SPECS, DAILY_REVIEW_OPERATION_SPECS, EXECUTION_INSPECT_OPERATION_SPECS, EXTERNAL_SESSION_OPERATION_SPECS, RUNTIME_POLICY_OPERATION_SPECS, RUNTIME_RESOURCE_OPERATION_SPECS, SCHEDULED_TASK_OPERATION_SPECS, PLAN_OPERATION_SPECS, PROJECT_CATALOG_OPERATION_SPECS, MESSAGE_OPERATION_SPECS, SESSION_TODO_OPERATION_SPECS, INTERACTION_OPERATION_SPECS, SESSION_CONTINUITY_OPERATION_SPECS, SESSION_TRANSCRIPT_OPERATION_SPECS, SESSION_TURNS_OPERATION_SPECS, SESSION_CATALOG_OPERATION_SPECS, SESSION_EFFECT_OPERATION_SPECS, SESSION_REVISION_OPERATION_SPECS, SESSION_BUNDLE_OPERATION_SPECS, SESSION_RETIREMENT_OPERATION_SPECS, ARTIFACT_OPERATION_SPECS, SKILL_CATALOG_OPERATION_SPECS, USAGE_PRICING_OPERATION_SPECS, MEMORY_OPERATION_SPECS, OAUTH_OPERATION_SPECS, EXTERNAL_AGENT_SETUP_OPERATION_SPECS, CLIENT_CAPABILITY_OPERATION_SPECS, WEB_SEARCH_OPERATION_SPECS, NETWORK_PROXY_OPERATION_SPECS, CONFIGURATION_OPERATION_SPECS, WORKHUB_COORDINATION_OPERATION_SPECS, PLUGIN_PLATFORM_OPERATION_SPECS);
101
+ export const HOST_OPERATION_SPECS = composeOperationSpecMaps(HOST_BOOTSTRAP_OPERATION_SPECS, HOST_RESOURCE_OPERATION_SPECS, PEER_MESH_OPERATION_SPECS, HOSTED_EXECUTION_OPERATION_SPECS, ACCESS_AUTHORITY_OPERATION_SPECS, SESSION_COLLABORATION_OPERATION_SPECS, AGENT_GRAPH_OPERATION_SPECS, GOAL_OPERATION_SPECS, TURN_OPERATION_SPECS, CONTEXT_OPERATION_SPECS, CONNECTION_EFFECT_OPERATION_SPECS, DAILY_REVIEW_OPERATION_SPECS, EXECUTION_INSPECT_OPERATION_SPECS, EXTERNAL_SESSION_OPERATION_SPECS, RUNTIME_POLICY_OPERATION_SPECS, RUNTIME_RESOURCE_OPERATION_SPECS, SCHEDULED_TASK_OPERATION_SPECS, PLAN_OPERATION_SPECS, PROJECT_CATALOG_OPERATION_SPECS, MESSAGE_OPERATION_SPECS, SESSION_TODO_OPERATION_SPECS, INTERACTION_OPERATION_SPECS, SESSION_CONTINUITY_OPERATION_SPECS, SESSION_TRANSCRIPT_OPERATION_SPECS, SESSION_TURNS_OPERATION_SPECS, SESSION_CATALOG_OPERATION_SPECS, SESSION_EFFECT_OPERATION_SPECS, SESSION_REVISION_OPERATION_SPECS, SESSION_BUNDLE_OPERATION_SPECS, SESSION_RETIREMENT_OPERATION_SPECS, ARTIFACT_OPERATION_SPECS, SKILL_CATALOG_OPERATION_SPECS, USAGE_PRICING_OPERATION_SPECS, MEMORY_OPERATION_SPECS, OAUTH_OPERATION_SPECS, EXTERNAL_AGENT_SETUP_OPERATION_SPECS, CLIENT_CAPABILITY_OPERATION_SPECS, WEB_SEARCH_OPERATION_SPECS, NETWORK_PROXY_OPERATION_SPECS, RECALL_OPERATION_SPECS, CONFIGURATION_OPERATION_SPECS, WORKHUB_COORDINATION_OPERATION_SPECS, PLUGIN_PLATFORM_OPERATION_SPECS);
102
102
  // Remote credentials are fail-closed: adding a protocol operation does not
103
103
  // grant it to remote owners until this policy is deliberately updated.
104
104
  export const REMOTE_OWNER_OPERATION_GRANTS = Object.freeze([
@@ -131,7 +131,6 @@ export const REMOTE_OWNER_OPERATION_GRANTS = Object.freeze([
131
131
  'connection.request-headers.query',
132
132
  'connection.request-headers.replace',
133
133
  'connection.test.run',
134
- 'connection.usage.read',
135
134
  'context.compact',
136
135
  'context.diagnostics.query',
137
136
  'credential.vault.delete',
@@ -139,7 +138,6 @@ export const REMOTE_OWNER_OPERATION_GRANTS = Object.freeze([
139
138
  'credential.vault.set',
140
139
  'daily-review.mutate',
141
140
  'daily-review.query',
142
- 'deep-research.query',
143
141
  'execution.inspect.query',
144
142
  'external-session.catalog.query',
145
143
  'external-session.import',
@@ -179,6 +177,7 @@ export const REMOTE_OWNER_OPERATION_GRANTS = Object.freeze([
179
177
  'runtime.policy.mutate',
180
178
  'runtime.policy.network-proxy.update',
181
179
  'runtime.policy.query',
180
+ 'recall.query',
182
181
  'runtime.resource.controller.acquire',
183
182
  'runtime.resource.controller.control',
184
183
  'runtime.resource.controller.release',
@@ -219,7 +218,6 @@ export const REMOTE_OWNER_OPERATION_GRANTS = Object.freeze([
219
218
  'turn.message.query',
220
219
  'turn.message.submit',
221
220
  'turn.query',
222
- 'turn.regenerate',
223
221
  'turn.resume.query',
224
222
  'turn.resume.start',
225
223
  'turn.start',
@@ -0,0 +1,270 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ /**
20
+ * `recall.query` — the retrieval surface a Client can ask for directly.
21
+ *
22
+ * Recall runs inside the Host because that is where the corpus lives: the
23
+ * Session manager, the distilled-fact store, and the material fetch are all
24
+ * Host-owned. A Client cannot perform it itself without pulling every
25
+ * transcript across the boundary, which is exactly the arrangement this
26
+ * operation replaces.
27
+ *
28
+ * The wire shape mirrors what the model's `Recall` tool already returns
29
+ * (`@maka/runtime/recall-tools`), with this protocol's camelCase field naming.
30
+ * That is deliberate: one recall answer, two presentations. A UI renders
31
+ * passages as navigable results and a model reads them as context, and neither
32
+ * gets a second implementation to drift from.
33
+ */
34
+ import { RECALL_MAX_LIMIT, RECALL_MAX_TERMS, RECALL_TOTAL_PAYLOAD_CAP_BYTES, } from '@maka/core/recall';
35
+ import { SEARCH_QUERY_MAX_CHARS } from '@maka/core/search';
36
+ import { requireCount, requireEncodedByteLimit, requireEntityId, requireShapedRecord, requireUtf8String, } from './codec.js';
37
+ import { invalidProtocolFrame } from './errors.js';
38
+ import { defineOperation } from './operation-spec.js';
39
+ export const RECALL_QUERY_TERM_MAX_CHARS = SEARCH_QUERY_MAX_CHARS;
40
+ /**
41
+ * Byte ceiling for one term. Core measures a term in code points
42
+ * (`Array.from(term).length`), so the wire bound is that count times the
43
+ * widest UTF-8 encoding of one code point. A term that passes here can still
44
+ * be rejected by core on the character count; this only stops a frame that
45
+ * could not represent a legal term at all.
46
+ */
47
+ const RECALL_QUERY_TERM_MAX_BYTES = RECALL_QUERY_TERM_MAX_CHARS * 4;
48
+ export const RECALL_QUERY_QUESTION_MAX_BYTES = 2 * 1024;
49
+ export const RECALL_QUERY_TEXT_MAX_BYTES = 32 * 1024;
50
+ export const RECALL_QUERY_MATERIAL_NAME_MAX_BYTES = 1024;
51
+ export const RECALL_QUERY_RESOURCE_MAX_BYTES = 1024;
52
+ /**
53
+ * Encoded ceiling for one envelope. Recall caps its own payload at
54
+ * `RECALL_TOTAL_PAYLOAD_CAP_BYTES`, but that counts passage text only; the
55
+ * envelope adds field names, message ids, timestamps and materials on top.
56
+ * This bound is that cap plus generous structural headroom, so it rejects a
57
+ * frame that no honest Host could have produced without constraining one that
58
+ * did.
59
+ */
60
+ export const RECALL_QUERY_RESULT_MAX_BYTES = RECALL_TOTAL_PAYLOAD_CAP_BYTES + 64 * 1024;
61
+ const RECALL_FAILURE_REASONS = [
62
+ 'invalid_query',
63
+ 'incognito_active',
64
+ 'not_found',
65
+ 'aborted',
66
+ ];
67
+ const RECALL_ROLES = ['user', 'assistant', 'tool'];
68
+ const RECALL_MATERIAL_KINDS = ['image', 'pdf', 'doc', 'code', 'other'];
69
+ const ERROR_CODES = [
70
+ 'host_not_ready',
71
+ 'host_draining',
72
+ 'operation_unavailable',
73
+ 'invalid_request',
74
+ 'internal_failure',
75
+ ];
76
+ export const RECALL_OPERATION_SPECS = {
77
+ 'recall.query': defineOperation({
78
+ mode: 'query',
79
+ availability: 'ready',
80
+ errors: ERROR_CODES,
81
+ decodeInput: decodeRecallQueryInput,
82
+ decodeOutput: decodeRecallQueryResult,
83
+ }),
84
+ };
85
+ function decodeRecallQueryInput(value) {
86
+ const record = requireShapedRecord(value, 'Recall query input', ['terms'], ['question', 'limit', 'sessionId', 'since', 'until']);
87
+ if (!Array.isArray(record.terms) || record.terms.length === 0) {
88
+ throw invalidProtocolFrame('Invalid Recall terms');
89
+ }
90
+ if (record.terms.length > RECALL_MAX_TERMS) {
91
+ throw invalidProtocolFrame('Too many Recall terms');
92
+ }
93
+ const terms = record.terms.map((term) => requireUtf8String(term, 'Recall term', RECALL_QUERY_TERM_MAX_BYTES));
94
+ const limit = record.limit === undefined ? undefined : requireCount(record.limit, 'Recall limit');
95
+ if (limit !== undefined && (limit < 1 || limit > RECALL_MAX_LIMIT)) {
96
+ throw invalidProtocolFrame('Invalid Recall limit');
97
+ }
98
+ return {
99
+ terms,
100
+ ...(record.question === undefined
101
+ ? {}
102
+ : {
103
+ question: requireUtf8String(record.question, 'Recall question', RECALL_QUERY_QUESTION_MAX_BYTES),
104
+ }),
105
+ ...(limit === undefined ? {} : { limit }),
106
+ ...(record.sessionId === undefined
107
+ ? {}
108
+ : { sessionId: requireEntityId(record.sessionId, 'Recall sessionId') }),
109
+ ...(record.since === undefined ? {} : { since: requireCount(record.since, 'Recall since') }),
110
+ ...(record.until === undefined ? {} : { until: requireCount(record.until, 'Recall until') }),
111
+ };
112
+ }
113
+ /**
114
+ * Bounded text that may be empty. Transcript text is legitimately empty when a
115
+ * message carried only files, and a Session title is empty for an unnamed
116
+ * Session; both are legal answers, so they must not be rejected as malformed.
117
+ */
118
+ function requireBoundedText(value, label, maxBytes) {
119
+ if (typeof value !== 'string' || Buffer.byteLength(value, 'utf8') > maxBytes) {
120
+ throw invalidProtocolFrame(`Invalid ${label}`);
121
+ }
122
+ return value;
123
+ }
124
+ function decodeRecallQueryResult(value) {
125
+ requireEncodedByteLimit(value, 'Recall query result', RECALL_QUERY_RESULT_MAX_BYTES);
126
+ const record = requireShapedRecord(value, 'Recall query result', ['ok'], ['facts', 'passages', 'gaps', 'searchedEverySession', 'reason', 'message']);
127
+ if (record.ok === false) {
128
+ const exact = requireShapedRecord(record, 'Recall error result', ['ok', 'reason', 'message'], []);
129
+ if (typeof exact.reason !== 'string' ||
130
+ !RECALL_FAILURE_REASONS.includes(exact.reason)) {
131
+ throw invalidProtocolFrame('Invalid Recall failure reason');
132
+ }
133
+ return {
134
+ ok: false,
135
+ reason: exact.reason,
136
+ message: requireUtf8String(exact.message, 'Recall failure message', RECALL_QUERY_TEXT_MAX_BYTES),
137
+ };
138
+ }
139
+ if (record.ok !== true)
140
+ throw invalidProtocolFrame('Invalid Recall query result kind');
141
+ const exact = requireShapedRecord(record, 'Recall success result', ['ok', 'facts', 'passages', 'gaps', 'searchedEverySession'], []);
142
+ if (!Array.isArray(exact.facts) || !Array.isArray(exact.passages)) {
143
+ throw invalidProtocolFrame('Invalid Recall success result');
144
+ }
145
+ if (typeof exact.searchedEverySession !== 'boolean') {
146
+ throw invalidProtocolFrame('Invalid Recall searchedEverySession');
147
+ }
148
+ return {
149
+ ok: true,
150
+ facts: exact.facts.map((fact) => decodeFact(fact)),
151
+ passages: exact.passages.map((passage) => decodePassage(passage)),
152
+ gaps: requireUtf8String(exact.gaps, 'Recall gaps', RECALL_QUERY_TEXT_MAX_BYTES),
153
+ searchedEverySession: exact.searchedEverySession,
154
+ };
155
+ }
156
+ function decodeFact(value) {
157
+ const record = requireShapedRecord(value, 'Recall fact', ['content', 'kind', 'observedAt'], []);
158
+ return {
159
+ content: requireUtf8String(record.content, 'Recall fact content', RECALL_QUERY_TEXT_MAX_BYTES),
160
+ kind: requireUtf8String(record.kind, 'Recall fact kind', RECALL_QUERY_MATERIAL_NAME_MAX_BYTES),
161
+ observedAt: requireCount(record.observedAt, 'Recall fact observedAt'),
162
+ };
163
+ }
164
+ function decodePassage(value) {
165
+ const record = requireShapedRecord(value, 'Recall passage', [
166
+ 'sessionId',
167
+ 'sessionTitle',
168
+ 'anchorMessageId',
169
+ 'sequence',
170
+ 'messages',
171
+ 'matchedTerms',
172
+ 'score',
173
+ 'hasMoreBefore',
174
+ 'hasMoreAfter',
175
+ ], ['turnId', 'lastMessageAt', 'truncated']);
176
+ if (!Array.isArray(record.messages) || !Array.isArray(record.matchedTerms)) {
177
+ throw invalidProtocolFrame('Invalid Recall passage messages');
178
+ }
179
+ if (typeof record.score !== 'number' || !Number.isFinite(record.score)) {
180
+ throw invalidProtocolFrame('Invalid Recall passage score');
181
+ }
182
+ if (typeof record.hasMoreBefore !== 'boolean' || typeof record.hasMoreAfter !== 'boolean') {
183
+ throw invalidProtocolFrame('Invalid Recall passage continuation flags');
184
+ }
185
+ return {
186
+ sessionId: requireEntityId(record.sessionId, 'Recall passage sessionId'),
187
+ sessionTitle: requireBoundedText(record.sessionTitle, 'Recall passage sessionTitle', RECALL_QUERY_TEXT_MAX_BYTES),
188
+ ...(record.turnId === undefined
189
+ ? {}
190
+ : { turnId: requireEntityId(record.turnId, 'Recall passage turnId') }),
191
+ anchorMessageId: requireEntityId(record.anchorMessageId, 'Recall passage anchorMessageId'),
192
+ sequence: requireCount(record.sequence, 'Recall passage sequence'),
193
+ messages: record.messages.map((message) => decodePassageMessage(message)),
194
+ matchedTerms: record.matchedTerms.map((term) => requireUtf8String(term, 'Recall matched term', RECALL_QUERY_TERM_MAX_BYTES)),
195
+ score: record.score,
196
+ ...(record.lastMessageAt === undefined
197
+ ? {}
198
+ : { lastMessageAt: requireCount(record.lastMessageAt, 'Recall passage lastMessageAt') }),
199
+ hasMoreBefore: record.hasMoreBefore,
200
+ hasMoreAfter: record.hasMoreAfter,
201
+ ...(record.truncated === undefined
202
+ ? {}
203
+ : record.truncated === true
204
+ ? { truncated: true }
205
+ : (() => {
206
+ throw invalidProtocolFrame('Invalid Recall passage truncated flag');
207
+ })()),
208
+ };
209
+ }
210
+ function decodePassageMessage(value) {
211
+ const record = requireShapedRecord(value, 'Recall passage message', ['messageId', 'role', 'matchKind', 'text', 'timestamp', 'isAnchor'], ['materials']);
212
+ if (typeof record.role !== 'string' ||
213
+ !RECALL_ROLES.includes(record.role)) {
214
+ throw invalidProtocolFrame('Invalid Recall passage message role');
215
+ }
216
+ if (typeof record.isAnchor !== 'boolean') {
217
+ throw invalidProtocolFrame('Invalid Recall passage message anchor flag');
218
+ }
219
+ if (record.materials !== undefined && !Array.isArray(record.materials)) {
220
+ throw invalidProtocolFrame('Invalid Recall passage message materials');
221
+ }
222
+ return {
223
+ messageId: requireEntityId(record.messageId, 'Recall passage messageId'),
224
+ role: record.role,
225
+ matchKind: requireUtf8String(record.matchKind, 'Recall passage message matchKind', RECALL_QUERY_MATERIAL_NAME_MAX_BYTES),
226
+ // A message whose whole content was a pasted file carries no text of its
227
+ // own; the material beside it is the point. Empty is legal here, unlike
228
+ // the identifiers around it.
229
+ text: requireBoundedText(record.text, 'Recall passage message text', RECALL_QUERY_TEXT_MAX_BYTES),
230
+ timestamp: requireCount(record.timestamp, 'Recall passage message timestamp'),
231
+ isAnchor: record.isAnchor,
232
+ ...(record.materials === undefined
233
+ ? {}
234
+ : { materials: record.materials.map((material) => decodeMaterial(material)) }),
235
+ };
236
+ }
237
+ function decodeMaterial(value) {
238
+ const record = requireShapedRecord(value, 'Recall material', ['name', 'kind', 'mimeType', 'bytes'], ['resource', 'sourceSessionId', 'materialId']);
239
+ if (typeof record.kind !== 'string' ||
240
+ !RECALL_MATERIAL_KINDS.includes(record.kind)) {
241
+ throw invalidProtocolFrame('Invalid Recall material kind');
242
+ }
243
+ const resource = record.resource === undefined
244
+ ? undefined
245
+ : requireUtf8String(record.resource, 'Recall material resource', RECALL_QUERY_RESOURCE_MAX_BYTES);
246
+ const sourceSessionId = record.sourceSessionId === undefined
247
+ ? undefined
248
+ : requireEntityId(record.sourceSessionId, 'Recall material sourceSessionId');
249
+ const materialId = record.materialId === undefined
250
+ ? undefined
251
+ : requireEntityId(record.materialId, 'Recall material materialId');
252
+ // An address says "read this now"; a location says "ask for it". Carrying
253
+ // both would offer a caller two answers to one question, so recall never
254
+ // emits both and the decoder refuses the shape rather than picking a winner.
255
+ if (resource !== undefined && (sourceSessionId !== undefined || materialId !== undefined)) {
256
+ throw invalidProtocolFrame('Invalid Recall material location');
257
+ }
258
+ if ((sourceSessionId === undefined) !== (materialId === undefined)) {
259
+ throw invalidProtocolFrame('Incomplete Recall material location');
260
+ }
261
+ return {
262
+ name: requireUtf8String(record.name, 'Recall material name', RECALL_QUERY_MATERIAL_NAME_MAX_BYTES),
263
+ kind: record.kind,
264
+ mimeType: requireUtf8String(record.mimeType, 'Recall material mimeType', RECALL_QUERY_MATERIAL_NAME_MAX_BYTES),
265
+ bytes: requireCount(record.bytes, 'Recall material bytes'),
266
+ ...(resource === undefined ? {} : { resource }),
267
+ ...(sourceSessionId === undefined ? {} : { sourceSessionId }),
268
+ ...(materialId === undefined ? {} : { materialId }),
269
+ };
270
+ }
@@ -273,7 +273,7 @@ export function decodeSessionCreateInput(value) {
273
273
  ...(target ? { modelTarget: target } : {}),
274
274
  ...(executorId ? { executorId } : {}),
275
275
  ...(Object.hasOwn(input, 'thinkingLevel')
276
- ? { thinkingLevel: thinkingLevel(input.thinkingLevel) }
276
+ ? { thinkingLevel: input.thinkingLevel === null ? null : thinkingLevel(input.thinkingLevel) }
277
277
  : {}),
278
278
  ...(Object.hasOwn(input, 'toolProfile')
279
279
  ? { toolProfile: sessionToolProfile(input.toolProfile) }
@@ -47,13 +47,7 @@ export const SESSION_TOOL_INTENT_MAX_BYTES = 512;
47
47
  export const SESSION_TOOL_ARGS_PREVIEW_MAX_BYTES = 8 * 1024;
48
48
  export const SESSION_SUBSCRIPTION_FRAME_MAX_BYTES = 64 * 1024 - 1;
49
49
  export const SESSION_RUNTIME_RESOURCE_PTY_DATA_MAX_BYTES = 48 * 1024;
50
- export const SESSION_DOMAINS = [
51
- 'todo',
52
- 'plan',
53
- 'deep_research',
54
- 'usage',
55
- 'runtime_resource',
56
- ];
50
+ export const SESSION_DOMAINS = ['todo', 'plan', 'usage', 'runtime_resource'];
57
51
  export const SESSION_RUNTIME_RESOURCE_CHANGES_MAX = 64;
58
52
  const SUBSCRIPTION_OPEN_ERRORS = [
59
53
  'transcript_preparing',
@@ -96,27 +96,6 @@ export const TURN_OPERATION_SPECS = {
96
96
  decodeInput: decodeTurnStopInput,
97
97
  decodeOutput: decodeTurnSnapshot,
98
98
  }),
99
- 'turn.regenerate': defineOperation({
100
- mode: 'command',
101
- availability: 'ready',
102
- errors: [
103
- 'host_not_ready',
104
- 'host_draining',
105
- 'operation_unavailable',
106
- 'not_found',
107
- 'session_archived',
108
- 'session_busy',
109
- 'operation_conflict',
110
- 'internal_failure',
111
- ],
112
- decodeInput: decodeTurnRegenerateInput,
113
- decodeOutput: decodeTurnSnapshot,
114
- assertOutputForInput: (input, output) => {
115
- if (input.sessionId !== output.sessionId || input.turnId !== output.turnId) {
116
- throw invalidProtocolFrame('Turn regenerate changed operation identity');
117
- }
118
- },
119
- }),
120
99
  'turn.resume.query': defineOperation({
121
100
  mode: 'query',
122
101
  availability: 'ready',
@@ -317,18 +296,6 @@ function decodeTurnStopInput(value) {
317
296
  runId: requireEntityId(record.runId, 'runId'),
318
297
  };
319
298
  }
320
- function decodeTurnRegenerateInput(value) {
321
- const record = requireExactRecord(value, 'turn.regenerate input', [
322
- 'sessionId',
323
- 'sourceTurnId',
324
- 'turnId',
325
- ]);
326
- return {
327
- sessionId: requireEntityId(record.sessionId, 'sessionId'),
328
- sourceTurnId: requireEntityId(record.sourceTurnId, 'sourceTurnId'),
329
- turnId: requireEntityId(record.turnId, 'turnId'),
330
- };
331
- }
332
299
  function decodeTurnResumeQueryInput(value) {
333
300
  const record = requireShapedRecord(value, 'turn.resume.query input', ['sessionId'], ['sourceRunId', 'expectedRuntimeEventHighWater']);
334
301
  if (record.expectedRuntimeEventHighWater !== undefined && record.sourceRunId === undefined) {