maka-agent 0.2.0-dev.12.20260901 → 0.2.0-dev.14.20260902

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 (105) hide show
  1. package/THIRD_PARTY_NOTICES.txt +2 -2
  2. package/dist/acp/maka-acp-agent.js +6 -3
  3. package/dist/acp/session-registry.js +307 -0
  4. package/dist/acp/stdio-server.js +20 -1
  5. package/dist/cli-core.js +15 -2
  6. package/dist/pi-transcript.js +1 -1
  7. package/dist/pi-tui-layout.js +1 -1
  8. package/dist/pi-tui-mcp-status.js +1 -1
  9. package/dist/pi-tui-runner.js +1 -1
  10. package/dist/runtime-host-access-command.js +37 -4
  11. package/dist/runtime-host-capability-provider-command.js +16 -2
  12. package/dist/runtime-host-cli-context.js +73 -3
  13. package/dist/runtime-host-cli.js +37 -6
  14. package/dist/runtime-host-run-command.js +3 -1
  15. package/dist/runtime-host-update-command.js +59 -31
  16. package/dist/tui-copy-catalog.js +4 -2
  17. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  18. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  19. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  20. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  21. package/node_modules/@maka/core/dist/attachments.js +12 -0
  22. package/node_modules/@maka/core/dist/connection-error-copy.js +17 -34
  23. package/node_modules/@maka/core/dist/events.js +0 -13
  24. package/node_modules/@maka/core/dist/maka-wordmark.js +23 -0
  25. package/node_modules/@maka/core/dist/model-catalog.js +22 -1
  26. package/node_modules/@maka/core/dist/runtime-event.js +1 -0
  27. package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +50 -1
  28. package/node_modules/@maka/core/dist/runtime-policy/credential-vault-codec.js +9 -1
  29. package/node_modules/@maka/core/dist/runtime-policy/model-catalog-entry-codec.js +10 -1
  30. package/node_modules/@maka/core/dist/runtime-policy/policy-codec.js +75 -0
  31. package/node_modules/@maka/core/dist/runtime-policy.js +4 -2
  32. package/node_modules/@maka/core/dist/session.js +1 -0
  33. package/node_modules/@maka/core/dist/settings.js +10 -2
  34. package/node_modules/@maka/core/dist/usage-record-schema.js +11 -1
  35. package/node_modules/@maka/core/package.json +2 -1
  36. package/node_modules/@maka/mcp/dist/index.js +1 -0
  37. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +52 -80
  38. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +178 -140
  39. package/node_modules/@maka/runtime/dist/archive-read-tool.js +26 -5
  40. package/node_modules/@maka/runtime/dist/builtin-tools.js +27 -2
  41. package/node_modules/@maka/runtime/dist/context-budget-policy.js +0 -12
  42. package/node_modules/@maka/runtime/dist/conversation-copy.js +66 -43
  43. package/node_modules/@maka/runtime/dist/durable-tool-result-projection.js +31 -0
  44. package/node_modules/@maka/runtime/dist/history-compact-error.js +10 -2
  45. package/node_modules/@maka/runtime/dist/history-compact-input-fit.js +7 -1
  46. package/node_modules/@maka/runtime/dist/history-compaction.js +13 -22
  47. package/node_modules/@maka/runtime/dist/model-history.js +41 -16
  48. package/node_modules/@maka/runtime/dist/provider-image-overflow-recovery.js +12 -31
  49. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +3 -0
  50. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +13 -2
  51. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -6
  52. package/node_modules/@maka/runtime/dist/shell-tools.js +77 -1
  53. package/node_modules/@maka/runtime/dist/tool-availability.js +74 -5
  54. package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +333 -52
  55. package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +34 -3
  56. package/node_modules/@maka/runtime/dist/tool-runtime.js +12 -0
  57. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +0 -3
  58. package/node_modules/@maka/runtime-host/dist/client/catalog-reader.js +40 -13
  59. package/node_modules/@maka/runtime-host/dist/client/connection.js +50 -31
  60. package/node_modules/@maka/runtime-host/dist/client/host-profile.js +48 -4
  61. package/node_modules/@maka/runtime-host/dist/client/index.js +2 -2
  62. package/node_modules/@maka/runtime-host/dist/client/owner-connection-code.js +32 -1
  63. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +22 -4
  64. package/node_modules/@maka/runtime-host/dist/operator/access-management-frame.js +10 -1
  65. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +56 -12
  66. package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +3 -0
  67. package/node_modules/@maka/runtime-host/dist/protocol/configuration.js +55 -9
  68. package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +37 -0
  69. package/node_modules/@maka/runtime-host/dist/protocol/index.js +17 -1
  70. package/node_modules/@maka/runtime-host/dist/protocol/network-proxy.js +1 -7
  71. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +2 -0
  72. package/node_modules/@maka/runtime-host/dist/protocol/runtime-policy.js +48 -1
  73. package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +4 -2
  74. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +2 -12
  75. package/node_modules/@maka/runtime-host/dist/server/access-authority.js +2 -1
  76. package/node_modules/@maka/runtime-host/dist/server/agent-graph-execution-coordinator.js +1 -2
  77. package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +0 -9
  78. package/node_modules/@maka/runtime-host/dist/server/configuration-coordinator.js +20 -1
  79. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +46 -24
  80. package/node_modules/@maka/runtime-host/dist/server/execution-service.js +4 -1
  81. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +15 -0
  82. package/node_modules/@maka/runtime-host/dist/server/listener-set.js +8 -4
  83. package/node_modules/@maka/runtime-host/dist/server/network-proxy-coordinator.js +0 -1
  84. package/node_modules/@maka/runtime-host/dist/server/peer-listener.js +3 -0
  85. package/node_modules/@maka/runtime-host/dist/server/runtime-policy-coordinator.js +28 -1
  86. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +3 -3
  87. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +35 -3
  88. package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +16 -0
  89. package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +189 -6
  90. package/node_modules/@maka/storage/dist/runtime-policy-stores.js +4 -1
  91. package/node_modules/@maka/storage/dist/settings-store.js +16 -25
  92. package/node_modules/fast-uri/index.js +215 -47
  93. package/node_modules/fast-uri/lib/schemes.js +9 -4
  94. package/node_modules/fast-uri/lib/utils.js +396 -92
  95. package/node_modules/fast-uri/package.json +1 -1
  96. package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
  97. package/node_modules/qs/CHANGELOG.md +15 -0
  98. package/node_modules/qs/README.md +19 -1
  99. package/node_modules/qs/dist/qs.js +21 -21
  100. package/node_modules/qs/eslint.config.mjs +1 -1
  101. package/node_modules/qs/lib/parse.js +3 -4
  102. package/node_modules/qs/lib/stringify.js +23 -8
  103. package/node_modules/qs/lib/utils.js +10 -3
  104. package/node_modules/qs/package.json +4 -4
  105. package/package.json +1 -1
@@ -54,6 +54,17 @@ export class RuntimeHostCliConflictError extends RuntimeHostPermanentReconnectEr
54
54
  }
55
55
  }
56
56
  export async function connectRuntimeHostCli(input, overrides = {}) {
57
+ const context = await connectRuntimeHostCliConnection(input, overrides);
58
+ try {
59
+ const catalog = await runAbortably(() => (overrides.readConnectionCatalog ?? readRuntimeHostConnectionCatalog)(context.connection), input.signal);
60
+ return { ...context, catalog };
61
+ }
62
+ catch (error) {
63
+ await context.close().catch(() => undefined);
64
+ throw error;
65
+ }
66
+ }
67
+ export async function connectRuntimeHostCliConnection(input, overrides = {}) {
57
68
  const deps = {
58
69
  connectOrSpawn: connectOrSpawnRuntimeHost,
59
70
  connectProfile: connectRuntimeHostProfile,
@@ -112,17 +123,18 @@ export async function connectRuntimeHostCli(input, overrides = {}) {
112
123
  }
113
124
  return connected.connection;
114
125
  };
126
+ let initialConnection;
115
127
  let connection;
116
128
  try {
117
- const initialConnection = await connect(undefined, input.interactiveSsh && process.stdin.isTTY && process.stdout.isTTY ? 'inherit' : 'batch');
129
+ initialConnection = await acquireAbortably(() => connect(input.signal, input.interactiveSsh && process.stdin.isTTY && process.stdout.isTTY ? 'inherit' : 'batch'), input.signal);
118
130
  connection = await createRuntimeHostReconnectingConnection({
119
131
  initialConnection,
120
132
  connect: (signal) => connect(signal, 'batch'),
121
133
  });
134
+ initialConnection = undefined;
122
135
  const liveConnection = connection;
123
136
  return {
124
137
  connection: liveConnection,
125
- catalog: await deps.readConnectionCatalog(liveConnection),
126
138
  profile,
127
139
  clientInstanceId,
128
140
  ...(resolvedProfile.profileIncarnationId
@@ -139,11 +151,69 @@ export async function connectRuntimeHostCli(input, overrides = {}) {
139
151
  };
140
152
  }
141
153
  catch (error) {
142
- await connection?.close().catch(() => undefined);
154
+ await (connection ?? initialConnection)?.close().catch(() => undefined);
143
155
  await peerClient?.close().catch(() => undefined);
144
156
  throw error;
145
157
  }
146
158
  }
159
+ function acquireAbortably(operation, signal) {
160
+ if (!signal)
161
+ return operation();
162
+ if (signal.aborted)
163
+ return Promise.reject(signal.reason);
164
+ return new Promise((resolve, reject) => {
165
+ let settled = false;
166
+ const settle = (callback) => {
167
+ if (settled)
168
+ return false;
169
+ settled = true;
170
+ signal.removeEventListener('abort', onAbort);
171
+ callback();
172
+ return true;
173
+ };
174
+ const onAbort = () => settle(() => reject(signal.reason));
175
+ signal.addEventListener('abort', onAbort, { once: true });
176
+ let running;
177
+ try {
178
+ running = operation();
179
+ }
180
+ catch (error) {
181
+ settle(() => reject(error));
182
+ return;
183
+ }
184
+ void running.then((value) => {
185
+ if (!settle(() => resolve(value)))
186
+ void value.close().catch(() => undefined);
187
+ }, (error) => settle(() => reject(error)));
188
+ });
189
+ }
190
+ function runAbortably(operation, signal) {
191
+ if (!signal)
192
+ return operation();
193
+ if (signal.aborted)
194
+ return Promise.reject(signal.reason);
195
+ return new Promise((resolve, reject) => {
196
+ let settled = false;
197
+ const settle = (callback) => {
198
+ if (settled)
199
+ return;
200
+ settled = true;
201
+ signal.removeEventListener('abort', onAbort);
202
+ callback();
203
+ };
204
+ const onAbort = () => settle(() => reject(signal.reason));
205
+ signal.addEventListener('abort', onAbort, { once: true });
206
+ let running;
207
+ try {
208
+ running = operation();
209
+ }
210
+ catch (error) {
211
+ settle(() => reject(error));
212
+ return;
213
+ }
214
+ void running.then((value) => settle(() => resolve(value)), (error) => settle(() => reject(error)));
215
+ });
216
+ }
147
217
  async function resolveHostProfile(input, deps) {
148
218
  if (input.profileId === undefined || input.profileId === LOCAL_RUNTIME_HOST_PROFILE.id) {
149
219
  return { profile: LOCAL_RUNTIME_HOST_PROFILE };
@@ -1878,10 +1878,14 @@ function projectRootValid(root, pathKind) {
1878
1878
  }
1879
1879
  function parseAccessCommand(argv) {
1880
1880
  const action = argv[0];
1881
- if (action !== 'list' && action !== 'issue' && action !== 'prepare' && action !== 'revoke') {
1881
+ if (action !== 'list' &&
1882
+ action !== 'issue' &&
1883
+ action !== 'prepare' &&
1884
+ action !== 'revoke' &&
1885
+ action !== 'connection-code') {
1882
1886
  return error(action
1883
1887
  ? `Unexpected runtime-host access command: ${action}`
1884
- : 'runtime-host access requires list, issue, prepare, or revoke');
1888
+ : 'runtime-host access requires connection-code, list, issue, prepare, or revoke');
1885
1889
  }
1886
1890
  let rootPath;
1887
1891
  let expectedRootId;
@@ -1892,6 +1896,7 @@ function parseAccessCommand(argv) {
1892
1896
  let credentialId;
1893
1897
  let capabilityOwnerCredentialId;
1894
1898
  let currentCredentialFingerprint;
1899
+ let name;
1895
1900
  const operationGrants = [];
1896
1901
  let canPublishClientCapabilities = false;
1897
1902
  let canUseHostPaths = false;
@@ -1914,6 +1919,7 @@ function parseAccessCommand(argv) {
1914
1919
  }
1915
1920
  if (argument === '--root' ||
1916
1921
  argument === '--expected-root' ||
1922
+ argument === '--name' ||
1917
1923
  argument === '--kind' ||
1918
1924
  argument === '--preset' ||
1919
1925
  argument === '--principal' ||
@@ -1928,6 +1934,8 @@ function parseAccessCommand(argv) {
1928
1934
  rootPath = parsed;
1929
1935
  if (argument === '--expected-root')
1930
1936
  expectedRootId = parsed;
1937
+ if (argument === '--name')
1938
+ name = parsed;
1931
1939
  if (argument === '--kind') {
1932
1940
  if (parsed !== 'remote-owner' && parsed !== 'capability-provider') {
1933
1941
  return error('--kind must be remote-owner or capability-provider');
@@ -1959,7 +1967,7 @@ function parseAccessCommand(argv) {
1959
1967
  if (expectedRootId && !/^[a-f0-9]{64}$/u.test(expectedRootId)) {
1960
1968
  return error('--expected-root must be a Runtime Host root identity');
1961
1969
  }
1962
- if (action === 'list') {
1970
+ if (action === 'connection-code') {
1963
1971
  if (principalId ||
1964
1972
  principalKindSpecified ||
1965
1973
  operationGrants.length > 0 ||
@@ -1969,6 +1977,27 @@ function parseAccessCommand(argv) {
1969
1977
  credentialId ||
1970
1978
  capabilityOwnerCredentialId ||
1971
1979
  currentCredentialFingerprint) {
1980
+ return error('Credential management options are not valid for access connection-code');
1981
+ }
1982
+ return {
1983
+ kind: 'runtime-host-access-connection-code',
1984
+ ...(rootPath ? { rootPath } : {}),
1985
+ ...(expectedRootId ? { expectedRootId } : {}),
1986
+ ...(name ? { name } : {}),
1987
+ framed,
1988
+ };
1989
+ }
1990
+ if (action === 'list') {
1991
+ if (principalId ||
1992
+ principalKindSpecified ||
1993
+ operationGrants.length > 0 ||
1994
+ canPublishClientCapabilities ||
1995
+ canUseHostPaths ||
1996
+ preset ||
1997
+ credentialId ||
1998
+ capabilityOwnerCredentialId ||
1999
+ currentCredentialFingerprint ||
2000
+ name) {
1972
2001
  return error('Credential mutation options are not valid for access list');
1973
2002
  }
1974
2003
  return {
@@ -1993,7 +2022,8 @@ function parseAccessCommand(argv) {
1993
2022
  canUseHostPaths ||
1994
2023
  preset ||
1995
2024
  credentialId ||
1996
- capabilityOwnerCredentialId) {
2025
+ capabilityOwnerCredentialId ||
2026
+ name) {
1997
2027
  return error('Credential issue options are not valid for access prepare');
1998
2028
  }
1999
2029
  return {
@@ -2008,7 +2038,7 @@ function parseAccessCommand(argv) {
2008
2038
  return error('--framed is only valid for access management');
2009
2039
  if (!principalId)
2010
2040
  return error('--principal is required');
2011
- if (credentialId || currentCredentialFingerprint) {
2041
+ if (credentialId || currentCredentialFingerprint || name) {
2012
2042
  return error('Credential target options are only valid for access revoke');
2013
2043
  }
2014
2044
  if (preset &&
@@ -2081,7 +2111,8 @@ function parseAccessCommand(argv) {
2081
2111
  operationGrants.length > 0 ||
2082
2112
  canPublishClientCapabilities ||
2083
2113
  canUseHostPaths ||
2084
- preset) {
2114
+ preset ||
2115
+ name) {
2085
2116
  return error('Issue-only access options are not valid for revoke');
2086
2117
  }
2087
2118
  return {
@@ -436,6 +436,7 @@ class TurnOutcomeClassifier {
436
436
  if (observation.outcome === 'sandbox_failure') {
437
437
  this.#unresolvedSandboxFailures.set(observation.toolUseId, {
438
438
  failedStepId: call?.stepId,
439
+ failedToolName: call?.toolName,
439
440
  });
440
441
  return;
441
442
  }
@@ -447,7 +448,8 @@ class TurnOutcomeClassifier {
447
448
  unresolved.length === 1 &&
448
449
  call?.stepId !== undefined &&
449
450
  unresolved[0]?.failedStepId !== undefined &&
450
- call.stepId !== unresolved[0].failedStepId) {
451
+ call.stepId !== unresolved[0].failedStepId &&
452
+ call.toolName === unresolved[0].failedToolName) {
451
453
  this.#unresolvedSandboxFailures.clear();
452
454
  this.#sandboxBoundaryRecovered = true;
453
455
  }
@@ -30,6 +30,13 @@ import { canDiscardRuntimeHostLifecycleDesiredArtifacts, replaceRuntimeHostLifec
30
30
  import { manageRuntimeHostManagedLifecycle } from './runtime-host-managed-lifecycle-manager.js';
31
31
  const OPERATOR_TIMEOUT_MS = 2 * 60_000;
32
32
  const OPERATOR_OUTPUT_MAX_BYTES = 256 * 1024;
33
+ function runtimeHostPackageUpdateOperation(input) {
34
+ if (input.currentVersion !== input.targetVersion ||
35
+ input.currentIntegrity !== input.targetIntegrity) {
36
+ return 'update';
37
+ }
38
+ return input.replaceExpectedHost ? 'replace_current' : 'already_current';
39
+ }
33
40
  class RuntimeHostUpdateSelectionError extends Error {
34
41
  code;
35
42
  constructor(code, message) {
@@ -55,6 +62,19 @@ export async function runManagedRuntimeHostUpdateCli(options, overrides = {}, fr
55
62
  createBackend: createPlatformRuntimeHostServiceBackend,
56
63
  verifyReady: verifyRuntimeHostManagedServiceReady,
57
64
  runOperator: runManagedRuntimeHostOperator,
65
+ canonical: {
66
+ createLifecycleDeps: (rootId) => ({
67
+ convergeOperator: (currentConfig, desiredConfig) => convergeRuntimeHostManagedOperator(currentConfig, desiredConfig),
68
+ verifyOperator: verifyRuntimeHostManagedOperator,
69
+ resolveProvider: (requested) => resolveRuntimeHostLifecycleProvider(rootId, requested),
70
+ }),
71
+ assertOperatorDeployment: assertRuntimeHostManagedOperatorDeployment,
72
+ recoverDeployment: resolveRecoverableRuntimeHostManagedDeployment,
73
+ verifyProjection: verifyRuntimeHostLifecycleProjection,
74
+ assertOperatorConfig: assertRuntimeHostManagedOperatorConfig,
75
+ manageLifecycle: manageRuntimeHostManagedLifecycle,
76
+ replaceLifecycle: replaceRuntimeHostLifecycle,
77
+ },
58
78
  writeOutput: (value) => process.stdout.write(value),
59
79
  writeError: (value) => process.stderr.write(value),
60
80
  ...overrides,
@@ -359,17 +379,13 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
359
379
  let staged;
360
380
  try {
361
381
  return await deps.withDeploymentLock(resolveRuntimeHostManagedControlRoot(options.managedRootId), async () => {
362
- await assertRuntimeHostManagedOperatorDeployment(options.managedRootId, options.operatorDeploymentId, process.argv[1] ?? '');
382
+ await deps.canonical.assertOperatorDeployment(options.managedRootId, options.operatorDeploymentId, process.argv[1] ?? '');
363
383
  const rejection = await deps.revalidateSelection();
364
384
  if (rejection) {
365
385
  throw new RuntimeHostUpdateSelectionError(rejection.code, rejection.message);
366
386
  }
367
- const lifecycleDeps = {
368
- convergeOperator: (currentConfig, desiredConfig) => convergeRuntimeHostManagedOperator(currentConfig, desiredConfig),
369
- verifyOperator: verifyRuntimeHostManagedOperator,
370
- resolveProvider: (requested) => resolveRuntimeHostLifecycleProvider(options.managedRootId, requested),
371
- };
372
- const recovered = await resolveRecoverableRuntimeHostManagedDeployment(options.managedRootId, lifecycleDeps, {
387
+ const lifecycleDeps = deps.canonical.createLifecycleDeps(options.managedRootId);
388
+ const recovered = await deps.canonical.recoverDeployment(options.managedRootId, lifecycleDeps, {
373
389
  expectedTarget: options.expectedTarget,
374
390
  ...(options.expectedHost ? { expectedOwner: options.expectedHost } : {}),
375
391
  allowInterruptActiveTasks: options.allowInterruptActiveTasks ?? false,
@@ -378,9 +394,9 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
378
394
  throw new RuntimeHostServiceManagerError('not_installed', 'The managed Runtime Host deployment is not installed');
379
395
  }
380
396
  const current = recovered.config;
381
- await verifyRuntimeHostLifecycleProjection(current, lifecycleDeps);
382
- assertRuntimeHostManagedOperatorConfig(current, options.operatorDeploymentId, process.argv[1] ?? '');
383
- const currentStatus = await manageRuntimeHostManagedLifecycle(options.managedRootId, {
397
+ await deps.canonical.verifyProjection(current, lifecycleDeps);
398
+ deps.canonical.assertOperatorConfig(current, options.operatorDeploymentId, process.argv[1] ?? '');
399
+ const currentStatus = await deps.canonical.manageLifecycle(options.managedRootId, {
384
400
  action: 'status',
385
401
  clientDataRoot: options.clientDataRoot,
386
402
  defaultRootPath: options.defaultRootPath,
@@ -406,8 +422,14 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
406
422
  if (!selectedDeploymentStillCurrent && !targetDeploymentIsCurrent) {
407
423
  throw new RuntimeHostServiceManagerError('target_mismatch', 'The managed Runtime Host changed after its update candidate was selected');
408
424
  }
409
- if (options.version === current.launch.package.version &&
410
- targetIntegrity === current.launch.package.integrity) {
425
+ const updateOperation = runtimeHostPackageUpdateOperation({
426
+ currentVersion: current.launch.package.version,
427
+ currentIntegrity: current.launch.package.integrity,
428
+ targetVersion: options.version,
429
+ targetIntegrity,
430
+ replaceExpectedHost: options.expectedHost !== undefined,
431
+ });
432
+ if (updateOperation === 'already_current') {
411
433
  await deps.prunePackages(current);
412
434
  emit({
413
435
  schemaVersion: 1,
@@ -420,15 +442,17 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
420
442
  return 0;
421
443
  }
422
444
  emit(progress('checking', current.launch.package.version, options.version));
423
- emit(progress('staging', current.launch.package.version, options.version));
424
- staged = await deps.prepareDeployment({
425
- serviceId: options.managedRootId,
426
- clientDataRoot: options.clientDataRoot,
427
- sourcePackageRoot: options.sourcePackageRoot,
428
- version: options.version,
429
- packageIntegrity: targetIntegrity,
430
- deploymentRoot: current.deploymentRoot,
431
- });
445
+ if (updateOperation === 'update') {
446
+ emit(progress('staging', current.launch.package.version, options.version));
447
+ staged = await deps.prepareDeployment({
448
+ serviceId: options.managedRootId,
449
+ clientDataRoot: options.clientDataRoot,
450
+ sourcePackageRoot: options.sourcePackageRoot,
451
+ version: options.version,
452
+ packageIntegrity: targetIntegrity,
453
+ deploymentRoot: current.deploymentRoot,
454
+ });
455
+ }
432
456
  const desired = {
433
457
  ...current,
434
458
  configRevision: current.configRevision + 1,
@@ -443,7 +467,7 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
443
467
  };
444
468
  emit(progress('retiring', current.launch.package.version, options.version));
445
469
  emit(progress('replacing', current.launch.package.version, options.version));
446
- const replacement = await replaceRuntimeHostLifecycle({
470
+ const replacement = await deps.canonical.replaceLifecycle({
447
471
  operation: 'update',
448
472
  current,
449
473
  desired,
@@ -459,8 +483,10 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
459
483
  deps: lifecycleDeps,
460
484
  });
461
485
  if (replacement.kind === 'active_tasks') {
462
- await staged.rollback();
463
- staged = undefined;
486
+ if (staged) {
487
+ await staged.rollback();
488
+ staged = undefined;
489
+ }
464
490
  emit({
465
491
  schemaVersion: 1,
466
492
  kind: 'result',
@@ -477,7 +503,7 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
477
503
  }
478
504
  staged = undefined;
479
505
  await deps.prunePackages(desired);
480
- const updated = await manageRuntimeHostManagedLifecycle(options.managedRootId, {
506
+ const updated = await deps.canonical.manageLifecycle(options.managedRootId, {
481
507
  action: 'status',
482
508
  clientDataRoot: options.clientDataRoot,
483
509
  defaultRootPath: options.defaultRootPath,
@@ -491,11 +517,13 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
491
517
  action: 'update',
492
518
  service: runtimeHostServiceSummary(updated),
493
519
  ...operatorCapabilities(),
494
- update: {
495
- kind: 'updated',
496
- previousVersion: current.launch.package.version,
497
- targetVersion: options.version,
498
- },
520
+ update: updateOperation === 'replace_current'
521
+ ? { kind: 'repaired', version: options.version }
522
+ : {
523
+ kind: 'updated',
524
+ previousVersion: current.launch.package.version,
525
+ targetVersion: options.version,
526
+ },
499
527
  });
500
528
  return 0;
501
529
  });
@@ -582,7 +610,7 @@ export async function runManagedRuntimeHostResolvedUpdateCli(options, selection,
582
610
  };
583
611
  try {
584
612
  if (selection.outcome.kind === 'manual_action' &&
585
- !(((options.expectedHost && options.allowInterruptActiveTasks) ||
613
+ !((options.expectedHost ||
586
614
  (options.allowManualUpdate &&
587
615
  options.managedRootId &&
588
616
  options.expectedTarget.deploymentId)) &&
@@ -23,6 +23,7 @@ export const TUI_COPY_RESOURCES = {
23
23
  copiedAll: 'Sent the conversation to the terminal via OSC 52 · {count, plural, one {# char} other {# chars}} — it reaches the clipboard only if your terminal (and tmux, if used) allows OSC 52 writes.',
24
24
  tooLarge: 'Too large to copy over OSC 52 · {bytes} bytes exceeds the {limit}-byte limit; a larger payload is silently dropped by the terminal.',
25
25
  nothingToCopy: 'Nothing to copy yet.',
26
+ usage: 'Usage: /copy [all]',
26
27
  roleUser: 'You:',
27
28
  roleAssistant: 'Maka:',
28
29
  roleGoalContinuation: 'Goal continuation (autonomous):',
@@ -33,6 +34,7 @@ export const TUI_COPY_RESOURCES = {
33
34
  copiedAll: '已将整段对话发送到终端 · {count} 个字符——仅当终端(以及 tmux,如使用)允许写入剪贴板时才会真正复制。',
34
35
  tooLarge: '内容过大,无法复制 · {bytes} 字节超过 {limit} 字节上限;更大的内容会被终端静默丢弃。',
35
36
  nothingToCopy: '暂无可复制的内容。',
37
+ usage: '用法:/copy [all]',
36
38
  roleUser: '你:',
37
39
  roleAssistant: 'Maka:',
38
40
  roleGoalContinuation: '目标续跑(自主):',
@@ -584,11 +586,11 @@ export const TUI_COPY_RESOURCES = {
584
586
  'session-actions': {
585
587
  en: {
586
588
  foreignScanFailed: 'Could not read external conversations: {detail}',
587
- newSessionFailed: 'Could not start a new session. Try again shortly.',
589
+ newSessionFailed: 'Could not start a new session: a local command could not be stopped. Press Ctrl+C to stop it, then try again.',
588
590
  },
589
591
  zh: {
590
592
  foreignScanFailed: '读取外部对话失败:{detail}',
591
- newSessionFailed: '无法开始新会话,请稍后重试。',
593
+ newSessionFailed: '无法开始新会话:无法停止本地命令。请按 Ctrl+C 停止命令后重试。',
592
594
  },
593
595
  },
594
596
  'pending-queue': {
@@ -61,6 +61,18 @@ export const MAX_ATTACHMENT_BYTES = 50 * 1024 * 1024;
61
61
  export const MAX_READ_IMAGE_BYTES = 5 * 1024 * 1024;
62
62
  export const MAX_MODEL_IMAGE_EDGE = 2000;
63
63
  export const READ_IMAGE_TOO_LARGE_MESSAGE = `Image exceeds the ${MAX_READ_IMAGE_BYTES / 1024 / 1024}MB model input limit; downscale it and try again.`;
64
+ /**
65
+ * What one image costs the request once materialization rehydrates it.
66
+ *
67
+ * A flat per-modality constant, because no character count answers this: both
68
+ * an artifact part and a legacy image result reduce to a one-line reference,
69
+ * and providers price an image by the area they resize it to. It sits above
70
+ * Anthropic's ~1,600-token ceiling for an image up to 1.15 megapixels and in
71
+ * the same range as the constants other agents use (opencode 1,500, Codex
72
+ * 1,844). Erring high is the safe direction: every consumer is reversible, so
73
+ * the worst an over-count buys is one compaction that was not needed.
74
+ */
75
+ export const MATERIALIZED_IMAGE_TOKENS = 2_000;
64
76
  /** Leaves room for Base64 expansion, text, and tool schemas under provider request limits. */
65
77
  export const MAX_PROVIDER_IMAGE_REQUEST_BYTES = 12 * 1024 * 1024;
66
78
  export const PROVIDER_IMAGE_BUDGET_EXCEEDED_MESSAGE = `Image was read, but the per-request image budget (${MAX_PROVIDER_IMAGE_REQUEST_BYTES / 1024 / 1024}MB across all images this turn) was exceeded; earlier images were sent and this one was omitted. Read fewer or smaller images.`;
@@ -17,43 +17,26 @@
17
17
  * under the License.
18
18
  */
19
19
  export const NO_REAL_CONNECTION_CODE = 'NO_REAL_CONNECTION';
20
- const GENERIC_FIX_COPY = '模型连接暂时无法用于发送,请到 设置 · 模型 检查后重试。';
21
20
  /**
22
- * The one hand-maintained table: reason → fix copy. Typed as
23
- * `Record<ChatConfigurationReason, string>`, so adding a reason to the union
24
- * fails the build until its copy is added here — completeness and copy live in
25
- * one place. `CHAT_CONFIGURATION_REASONS` and the parser's known-token set are
26
- * derived from its keys, so neither can drift from it.
21
+ * Every reason, mirroring the canonical union in `connection-readiness`.
22
+ * `satisfies` rejects tokens that are not in the union, and the
23
+ * `AssertNever` alias below fails the build when the union gains a reason
24
+ * this list is missing, so the parser's known-token set cannot silently
25
+ * drift from the taxonomy.
27
26
  */
28
- const REASON_FIX_COPY = {
29
- missing_default_connection: '等待配置默认模型。请到 设置 · 模型 添加一个可用模型连接后再发送。',
30
- connection_missing: '该任务依赖的模型连接已删除,请到 设置 · 模型 重新选择或重建连接。',
31
- connection_disabled: '当前模型连接已禁用。请到 设置 · 模型 启用或选择其他默认模型。',
32
- missing_api_key: '当前模型连接还没有可用凭据。请到 设置 · 模型 补齐 API key 或重新登录后再发送。',
33
- missing_model: '当前模型连接还没有可用模型。请到 设置 · 模型 选择默认模型后再发送。',
34
- empty_model_list: '当前模型连接没有启用模型。请到 设置 · 模型 添加或启用模型后再发送。',
35
- model_not_enabled: '当前任务选择的模型未启用。请到 设置 · 模型 重新选择可用模型后再发送。',
36
- model_not_chat_capable: '当前任务选择的模型不能用于聊天。请到 设置 · 模型 重新选择支持聊天的模型后再发送。',
37
- fake_backend: '当前任务来自旧的本地模拟连接。请到 设置 · 模型 添加真实模型后新建任务。',
38
- provider_retired: '当前模型连接的登录方式已从 Maka 移除,无法用于发送。请到 设置 · 模型 改用其他连接后再发送。',
39
- };
40
- /**
41
- * Every reason, derived from the copy table so test coverage and the parser's
42
- * known-token set track the union automatically. Module-scoped (the package
43
- * index does not re-export it) — only the parser and the tests read it.
44
- */
45
- export const CHAT_CONFIGURATION_REASONS = Object.keys(REASON_FIX_COPY);
27
+ export const CHAT_CONFIGURATION_REASONS = [
28
+ 'missing_default_connection',
29
+ 'connection_missing',
30
+ 'connection_disabled',
31
+ 'missing_api_key',
32
+ 'missing_model',
33
+ 'empty_model_list',
34
+ 'model_not_enabled',
35
+ 'model_not_chat_capable',
36
+ 'fake_backend',
37
+ 'provider_retired',
38
+ ];
46
39
  const KNOWN_CHAT_CONFIGURATION_REASONS = new Set(CHAT_CONFIGURATION_REASONS);
47
- /**
48
- * Fix instructions for a not-ready connection. `undefined` (a missing or
49
- * unrecognized reason) returns the generic fallback; every known reason has its
50
- * own line, guaranteed present by the `Record` type above.
51
- */
52
- export function describeChatConfigurationReason(reason) {
53
- return reason !== undefined && KNOWN_CHAT_CONFIGURATION_REASONS.has(reason)
54
- ? REASON_FIX_COPY[reason]
55
- : GENERIC_FIX_COPY;
56
- }
57
40
  // `\bNO_REAL_CONNECTION\b` pins the whole code: the trailing boundary stops it
58
41
  // matching a longer word like `NO_REAL_CONNECTIONS` (the reason group is
59
42
  // optional, so without the boundary that prefix alone would falsely match and
@@ -372,24 +372,11 @@ export class ToolOutcomeUnknownError extends Error {
372
372
  this.name = 'ToolOutcomeUnknownError';
373
373
  }
374
374
  }
375
- export const CONTEXT_BUDGET_EXHAUSTED_DETAILS = [
376
- 'no_safe_completed_span',
377
- 'summarizer_failed',
378
- 'malformed_summary_missing_section',
379
- 'malformed_summary_truncated',
380
- 'malformed_summary_too_small_for_fold',
381
- 'head_anchor_exceeds_capacity',
382
- ];
383
- export function isContextBudgetExhaustedDetail(value) {
384
- return CONTEXT_BUDGET_EXHAUSTED_DETAILS.includes(value);
385
- }
386
375
  /** Stable failure taxonomy for complete events that did not finish the turn. */
387
376
  export function failureClassFromCompleteStopReason(reason) {
388
377
  if (reason === 'error')
389
378
  return 'runtime_error';
390
379
  if (reason === 'step_limit')
391
380
  return 'tool_step_cap_reached';
392
- if (reason === 'context_budget_exhausted')
393
- return 'context_budget_exhausted';
394
381
  return undefined;
395
382
  }
@@ -0,0 +1,23 @@
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
+ * Traced outline of Maka's product wordmark in its canonical 460×120 view box.
21
+ * Renderers choose color through `currentColor`; the brand geometry has one owner.
22
+ */
23
+ export const MAKA_WORDMARK_PATH = 'M2639 1187 c-38 -29 -39 -46 -39 -479 0 -400 1 -425 19 -455 23 -37 68 -50 108 -31 41 20 53 53 53 154 0 83 2 92 25 114 l24 23 143 -138 c79 -75 156 -143 171 -151 57 -30 127 14 127 79 0 32 -39 75 -217 238 l-82 75 130 103 c157 125 173 152 123 210 -21 25 -34 31 -65 31 -35 0 -55 -13 -209 -140 l-170 -139 0 229 0 228 -26 31 c-20 24 -34 31 -62 31 -21 0 -44 -6 -53 -13z M1926 969 c-109 -26 -216 -114 -264 -217 -24 -50 -27 -69 -27 -162 0 -95 3 -111 28 -162 39 -79 104 -143 185 -181 62 -29 75 -32 168 -32 94 0 105 2 164 33 35 18 64 31 64 30 18 -50 63 -74 111 -58 57 19 60 32 57 258 -2 176 -6 211 -23 258 -24 63 -100 151 -163 188 -84 49 -205 67 -300 45z m142 -170 c93 -20 171 -113 172 -205 0 -58 -45 -140 -97 -177 -112 -80 -278 -26 -328 107 -43 112 34 247 158 276 45 11 41 11 95 -1z M547 960 c-105 -18 -200 -90 -248 -187 -23 -45 -24 -60 -27 -268 -2 -120 -1 -229 3 -242 7 -30 58 -56 94 -48 16 3 38 16 49 28 19 20 21 36 24 227 3 226 8 248 69 290 69 50 157 44 215 -14 46 -46 54 -88 54 -297 0 -179 1 -186 23 -207 43 -40 100 -37 134 7 9 11 12 71 13 201 0 102 5 202 10 222 32 114 172 160 264 87 55 -44 60 -66 61 -284 1 -110 5 -208 9 -218 13 -27 63 -49 97 -42 16 4 38 18 49 32 19 24 20 40 20 228 0 224 -8 268 -61 348 -60 90 -158 139 -280 139 -62 1 -88 -4 -135 -26 -33 -15 -71 -38 -85 -52 l-27 -26 -50 36 c-82 60 -179 83 -275 66z M3659 960 c-137 -23 -264 -138 -299 -268 -53 -202 61 -407 260 -466 102 -30 242 -14 304 35 15 12 28 20 29 18 1 -2 7 -13 13 -24 26 -48 94 -55 135 -14 19 19 20 30 17 237 -3 214 -3 218 -31 273 -50 103 -163 186 -282 208 -65 12 -79 12 -146 1z m114 -201 c33 -65 48 -81 107 -112 59 -31 61 -49 10 -72 -52 -24 -93 -70 -115 -131 -10 -27 -24 -56 -32 -64 -11 -12 -15 -12 -26 0 -8 8 -19 35 -26 59 -14 48 -67 110 -121 139 -19 10 -35 25 -35 33 0 7 21 23 46 35 52 25 106 82 115 122 8 34 24 54 38 49 6 -2 23 -28 39 -58z';
@@ -208,16 +208,33 @@ export function resolveConnectionModelCatalog(connection) {
208
208
  * The other branch is the client-side resolution an editor legitimately needs:
209
209
  * once the draft diverges — model rows just fetched, ids just ticked — it
210
210
  * describes a connection the Host has never been told about and so cannot
211
- * have resolved.
211
+ * have resolved. Even then, metadata coverage is a fact about the id, not the
212
+ * edited row: the Host settled `describedByMetadata` against its (possibly
213
+ * refreshed) catalog, and the client's bundled table — the stale authority
214
+ * this field exists to stop trusting — must not overturn it. So the local
215
+ * rebuild keeps the Host's answer for every id it already described; only an
216
+ * id the Host has never seen falls back to the locally computed value.
212
217
  */
213
218
  export function resolveDraftConnectionModelCatalog(connection, draft) {
214
219
  if (draftMatchesConnection(connection, draft))
215
220
  return connection.catalogEntries;
221
+ const hostCoverage = new Map(connection.catalogEntries.map((entry) => [
222
+ entry.id,
223
+ entry.describedByMetadata,
224
+ ]));
216
225
  return resolveConnectionModelCatalog({
217
226
  ...connection,
218
227
  enabledModelIds: [...draft.enabledModelIds],
219
228
  models: draft.modelSource === 'fetched' || draft.models.length > 0 ? [...draft.models] : undefined,
220
229
  modelSource: draft.modelSource,
230
+ }).map((entry) => {
231
+ // Sync point: `describedByMetadata` has two producers — `makeEntry` above
232
+ // and this overlay — so a future field decided on the Host must be patched
233
+ // back here too, or the local rebuild will silently downgrade it.
234
+ const hostDescribed = hostCoverage.get(entry.id);
235
+ if (hostDescribed === undefined || hostDescribed === entry.describedByMetadata)
236
+ return entry;
237
+ return { ...entry, describedByMetadata: hostDescribed };
221
238
  });
222
239
  }
223
240
  function draftMatchesConnection(connection, draft) {
@@ -313,6 +330,10 @@ function makeEntry(ctx, model, overrides = {}) {
313
330
  isDefault: overrides.isDefault ?? normalizedModel.id === normalizedDefaultModel,
314
331
  supportsVision: capabilities.vision === true,
315
332
  thinkingLevels: thinkingVariantsForConnection(thinkingContext, normalizedModel.id),
333
+ // Whether metadata describes this id at all — the same question
334
+ // `hasModelMetadata` answers, decided here on the Host's catalog so a client
335
+ // need not re-ask its own bundled table (which a Host refresh never reaches).
336
+ describedByMetadata: Object.keys(metadata).length > 0,
316
337
  ...(contextWindow !== undefined ? { contextWindow } : {}),
317
338
  ...(knowledgeCutoff !== undefined ? { knowledgeCutoff } : {}),
318
339
  };
@@ -255,6 +255,7 @@ const RUNTIME_TOKEN_USAGE_SHAPE = defineObjectShape()(['input', 'output'], [
255
255
  'promptSegments',
256
256
  'contextBudget',
257
257
  'providerRequestTraceId',
258
+ 'lastRequestAnchor',
258
259
  ]);
259
260
  const RUNTIME_REFS_SHAPE = defineObjectShape()([], [
260
261
  'storedMessageId',