maka-agent 0.2.0-dev.25.20260908 → 0.2.0-dev.26.20260909

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 (65) hide show
  1. package/dist/mcp-capability-provider.js +4 -1
  2. package/dist/pi-tui-mcp-status.js +2 -2
  3. package/dist/pi-tui-pickers.js +19 -29
  4. package/dist/tui-autocomplete-layout.js +3 -1
  5. package/dist/tui-copy-catalog.js +6 -3
  6. package/dist/tui-editor-render.js +36 -0
  7. package/dist/tui-mcp-control.js +41 -6
  8. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  9. package/node_modules/@maka/core/dist/runtime-event.js +3 -0
  10. package/node_modules/@maka/core/dist/session.js +29 -4
  11. package/node_modules/@maka/core/dist/workhub-action-result.js +68 -0
  12. package/node_modules/@maka/core/package.json +2 -3
  13. package/node_modules/@maka/eval/dist/toolchain-verification.js +1 -1
  14. package/node_modules/@maka/mcp/dist/form-elicitation.js +213 -0
  15. package/node_modules/@maka/mcp/dist/index.js +337 -71
  16. package/node_modules/@maka/runtime/dist/agent-run.js +10 -1
  17. package/node_modules/@maka/runtime/dist/goal-continuation.js +5 -1
  18. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +20 -1
  19. package/node_modules/@maka/runtime/dist/runtime-kernel.js +120 -13
  20. package/node_modules/@maka/runtime/dist/session-manager.js +20 -36
  21. package/node_modules/@maka/runtime-host/dist/client/connection.js +30 -5
  22. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +17 -1
  23. package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +4 -2
  24. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +18 -1
  25. package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
  26. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +3 -2
  27. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -0
  28. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +68 -168
  29. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -0
  30. package/node_modules/@maka/runtime-host/dist/server/canonical-session-projection.js +2 -1
  31. package/node_modules/@maka/runtime-host/dist/server/connection-session.js +28 -8
  32. package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +112 -56
  33. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +45 -20
  34. package/node_modules/@maka/runtime-host/dist/server/failure-diagnostic.js +24 -0
  35. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +7 -3
  36. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -3
  37. package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +5 -2
  38. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +3 -2
  39. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +34 -4
  40. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +4 -1
  41. package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +2 -7
  42. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +110 -14
  43. package/node_modules/@maka/runtime-host/dist/server/runtime-resource-coordinator.js +11 -8
  44. package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +31 -21
  45. package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +1 -1
  46. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +20 -8
  47. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +15 -104
  48. package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +3 -0
  49. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -85
  50. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +120 -137
  51. package/node_modules/@maka/storage/dist/agent-run-store.js +14 -1
  52. package/node_modules/@maka/storage/dist/artifact-store.js +40 -3
  53. package/node_modules/@maka/storage/dist/atomic-file-write.js +74 -0
  54. package/node_modules/@maka/storage/dist/credential-store.js +12 -63
  55. package/node_modules/@maka/storage/dist/execution-stores.js +4 -1
  56. package/node_modules/@maka/storage/dist/mcp-config-store.js +10 -25
  57. package/node_modules/@maka/storage/dist/session-store.js +14 -2
  58. package/node_modules/@maka/storage/dist/settings-store.js +8 -4
  59. package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +11 -0
  60. package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +12 -1
  61. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +67 -3
  62. package/node_modules/@maka/storage/dist/stable-storage.js +14 -1
  63. package/package.json +1 -1
  64. package/node_modules/@maka/core/dist/workhub-creation-intent.js +0 -990
  65. package/node_modules/@maka/core/dist/workhub-session-resolver.js +0 -50
@@ -73,7 +73,10 @@ export function createMcpCapabilityProvider(manager) {
73
73
  if (!binding)
74
74
  throw new Error('MCP capability is not part of the published snapshot');
75
75
  await options.accept({ kind: 'none' });
76
- return projectMcpResult(await manager.callTool(binding, frame.arguments, { signal: options.signal }));
76
+ return projectMcpResult(await manager.callTool(binding, frame.arguments, {
77
+ signal: options.signal,
78
+ requestInteraction: options.requestInteraction,
79
+ }));
77
80
  },
78
81
  };
79
82
  }
@@ -210,7 +210,7 @@ export class McpManagementOverlay {
210
210
  return;
211
211
  const protocol = matchesKey(data, '1')
212
212
  ? 'legacy'
213
- : matchesKey(data, '2')
213
+ : matchesKey(data, '2') || matchesKey(data, 'enter')
214
214
  ? 'auto'
215
215
  : matchesKey(data, '3')
216
216
  ? '2026-07-28'
@@ -371,7 +371,7 @@ export class McpManagementOverlay {
371
371
  return [ansi.bold(editor.transportTitle), '', '1 stdio', '2 Streamable HTTP'];
372
372
  }
373
373
  if (this.phase.kind === 'protocol') {
374
- return [ansi.bold(editor.protocolTitle), '', '1 legacy', '2 auto', '3 2026-07-28'];
374
+ return [ansi.bold(editor.protocolTitle), '', '1 legacy', '2 auto (Enter)', '3 2026-07-28'];
375
375
  }
376
376
  if (this.phase.kind === 'confirm_add') {
377
377
  return confirmAddDocument(this.phase.draft, this.input.locale);
@@ -21,6 +21,7 @@ import { defineUiMessageCatalog, formatUiMessage, resolveUiMessageCatalog, } fro
21
21
  import { providerDefaultsOf, validateSlug, } from '@maka/core/llm-connections';
22
22
  import { CONNECTION_NAME_MAX_LENGTH } from '@maka/core/runtime-policy';
23
23
  import { ansi, editorTheme, selectListTheme, stripAnsi } from './tui-ansi.js';
24
+ import { stripUnfocusedCursorStyle } from './tui-editor-render.js';
24
25
  import { TUI_COPY_RESOURCES } from './tui-copy-catalog.js';
25
26
  const TUI_PICKER_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()(TUI_COPY_RESOURCES.pickers));
26
27
  export function onboardingFailureMessage(failure, locale) {
@@ -433,22 +434,13 @@ export class UserQuestionOverlay {
433
434
  return assemble(cappedTitle, optionRows.map((rows) => clampRowsWithEllipsis(rows, perOption, safeWidth)));
434
435
  }
435
436
  renderInputRow(width) {
436
- const prefix = this.onInputRow ? '→ ' : ' ';
437
- const contentWidth = Math.max(1, width - USER_QUESTION_ROW_PREFIX_WIDTH);
438
- // Focused only while the input row is highlighted: that both shows the block
439
- // cursor and emits the hardware-cursor marker (#1064) so IME candidate windows
440
- // anchor to the edited text instead of the terminal bottom.
437
+ const marker = this.onInputRow ? '→' : ' ';
438
+ // Focus controls the IME marker and our cursor-visibility adapter (#1064).
441
439
  this.editor.focused = this.onInputRow;
442
440
  if (!this.onInputRow && this.editor.getText().length === 0) {
443
- return [padLine(`${prefix}${ansi.dim(this.input.placeholder)}`, width)];
441
+ return [padLine(`${marker} ${ansi.dim(this.input.placeholder)}`, width)];
444
442
  }
445
- // Drop the editor's own top/bottom border rows; keep just its content lines
446
- // so the answer reads as one row of the list.
447
- const editorLines = this.editor.render(contentWidth).slice(1, -1);
448
- if (editorLines.length === 0) {
449
- return [padLine(`${prefix}${ansi.dim(this.input.placeholder)}`, width)];
450
- }
451
- return editorLines.map((line, index) => padLine(`${index === 0 ? prefix : ' '}${line}`, width));
443
+ return renderFieldRow(this.editor, marker, width);
452
444
  }
453
445
  }
454
446
  export function modelPickerItems(currentModel, models, locale) {
@@ -750,14 +742,7 @@ export class ModelSearchOverlay {
750
742
  ];
751
743
  }
752
744
  renderFieldRow(editor, label, width) {
753
- const prefix = `${label} `;
754
- const prefixWidth = visibleWidth(prefix);
755
- const contentWidth = Math.max(1, width - prefixWidth);
756
- const editorLines = editor.render(contentWidth).slice(1, -1);
757
- if (editorLines.length === 0) {
758
- return [padLine(prefix, width)];
759
- }
760
- return editorLines.map((line, index) => padLine(`${index === 0 ? prefix : ' '.repeat(prefixWidth)}${line}`, width));
745
+ return renderFieldRow(editor, label, width);
761
746
  }
762
747
  }
763
748
  /**
@@ -873,6 +858,18 @@ function padLine(text, width) {
873
858
  const trimmed = visibleWidth(text) > safeWidth ? truncateToWidth(text, safeWidth, '') : text;
874
859
  return `${trimmed}${' '.repeat(Math.max(0, safeWidth - visibleWidth(trimmed)))}`;
875
860
  }
861
+ function renderFieldRow(editor, label, width) {
862
+ const prefix = `${label} `;
863
+ const prefixWidth = visibleWidth(prefix);
864
+ const contentWidth = Math.max(1, width - prefixWidth);
865
+ // These field editors have no autocomplete rows. Keep Editor's wrapping and
866
+ // scrolling, but omit its top/bottom borders.
867
+ const lines = editor.render(contentWidth).slice(1, -1);
868
+ const editorLines = stripUnfocusedCursorStyle(lines, editor.focused);
869
+ if (editorLines.length === 0)
870
+ return [padLine(prefix, width)];
871
+ return editorLines.map((line, index) => padLine(`${index === 0 ? prefix : ' '.repeat(prefixWidth)}${line}`, width));
872
+ }
876
873
  function keyEntryHint(copy, hasConnection, returnsTo) {
877
874
  const action = hasConnection ? 'reuse' : 'enter';
878
875
  return copy.keyHints[action][returnsTo];
@@ -1546,13 +1543,6 @@ export class OnboardingWizard {
1546
1543
  ];
1547
1544
  }
1548
1545
  renderFieldRow(editor, label, width) {
1549
- const prefix = `${label} `;
1550
- const prefixWidth = visibleWidth(prefix);
1551
- const contentWidth = Math.max(1, width - prefixWidth);
1552
- const editorLines = editor.render(contentWidth).slice(1, -1);
1553
- if (editorLines.length === 0) {
1554
- return [padLine(prefix, width)];
1555
- }
1556
- return editorLines.map((line, index) => padLine(`${index === 0 ? prefix : ' '.repeat(prefixWidth)}${line}`, width));
1546
+ return renderFieldRow(editor, label, width);
1557
1547
  }
1558
1548
  }
@@ -17,6 +17,7 @@
17
17
  * under the License.
18
18
  */
19
19
  import { selectListTheme, stripAnsi } from './tui-ansi.js';
20
+ import { stripUnfocusedCursorStyle } from './tui-editor-render.js';
20
21
  export function arrangeAutocompleteAboveEditor(input) {
21
22
  if (!input.autocompleteShowing) {
22
23
  return { lines: input.lines, autocompleteSlotRows: 0, editorRows: input.lines.length };
@@ -72,7 +73,8 @@ export class MakaAutocompleteAboveEditorComponent {
72
73
  this.editor.handleInput(data);
73
74
  }
74
75
  render(width) {
75
- const lines = this.editor.render(width);
76
+ const renderedLines = this.editor.render(width);
77
+ const lines = stripUnfocusedCursorStyle(renderedLines, this.editor.focused);
76
78
  const result = arrangeAutocompleteAboveEditor({
77
79
  lines,
78
80
  autocompleteShowing: this.editor.isShowingAutocomplete(),
@@ -326,6 +326,7 @@ export const TUI_COPY_RESOURCES = {
326
326
  'credential-cleanup-failed': 'Stored credentials could not be removed; the configuration was not changed.',
327
327
  'publication-credential-failed': 'The provider credential could not be stored or applied.',
328
328
  'persist-failed': 'The configuration could not be saved.',
329
+ 'commit-unknown': 'Configuration published; crash durability is uncertain. Check synchronization before retrying.',
329
330
  'manager-failed': 'The MCP connection action failed.',
330
331
  turn_active: 'MCP cannot be changed while a turn or another control action is running.',
331
332
  invalid: 'Check the value and try again.',
@@ -424,6 +425,7 @@ export const TUI_COPY_RESOURCES = {
424
425
  'credential-cleanup-failed': '无法删除旧凭据,配置未修改。',
425
426
  'publication-credential-failed': '无法保存或应用 Provider 凭据。',
426
427
  'persist-failed': '无法保存配置。',
428
+ 'commit-unknown': '配置已发布,但无法确认崩溃后的持久性。请先检查同步状态再重试。',
427
429
  'manager-failed': 'MCP 连接操作失败。',
428
430
  turn_active: 'Turn 或其他控制操作运行期间不能修改 MCP。',
429
431
  invalid: '请检查输入后重试。',
@@ -522,6 +524,7 @@ export const TUI_COPY_RESOURCES = {
522
524
  'credential-cleanup-failed': '無法移除已儲存的認證資料;設定未變更。',
523
525
  'publication-credential-failed': '無法儲存或套用 Provider 認證資料。',
524
526
  'persist-failed': '無法儲存設定。',
527
+ 'commit-unknown': '設定已發布,但無法確認當機後的持久性。請先檢查同步狀態再重試。',
525
528
  'manager-failed': 'MCP 連線操作失敗。',
526
529
  turn_active: 'Turn 或其他控制操作執行期間無法修改 MCP。',
527
530
  invalid: '請檢查輸入後重試。',
@@ -620,7 +623,7 @@ export const TUI_COPY_RESOURCES = {
620
623
  pickers: {
621
624
  en: {
622
625
  modelPickerTitle: 'Select Model',
623
- modelSwitchCacheWarning: ' Switching models may rebuild the prompt cache; the next request may be slower or cost more.',
626
+ modelSwitchCacheWarning: '\u26a0 Switching models may rebuild the prompt cache; the next request may be slower or cost more.',
624
627
  modelSearchHint: 'Search models / providers / connections · ↑↓ select · Enter confirm · Esc cancel',
625
628
  searchLabel: 'Search',
626
629
  noMatchingModels: 'No matching models',
@@ -715,7 +718,7 @@ export const TUI_COPY_RESOURCES = {
715
718
  },
716
719
  'zh-CN': {
717
720
  modelPickerTitle: '选择模型',
718
- modelSwitchCacheWarning: ' 切换模型可能需要重建提示缓存;下一次请求可能更慢或成本更高。',
721
+ modelSwitchCacheWarning: '\u26a0 切换模型可能需要重建提示缓存;下一次请求可能更慢或成本更高。',
719
722
  modelSearchHint: '搜索模型 / 服务商 / 连接 · ↑↓ 选择 · Enter 确认 · Esc 取消',
720
723
  searchLabel: '搜索',
721
724
  noMatchingModels: '没有匹配的模型',
@@ -809,7 +812,7 @@ export const TUI_COPY_RESOURCES = {
809
812
  },
810
813
  'zh-TW': {
811
814
  modelPickerTitle: '選擇模型',
812
- modelSwitchCacheWarning: ' 切換模型可能需要重建提示快取;下一次請求可能較慢或成本較高。',
815
+ modelSwitchCacheWarning: '\u26a0 切換模型可能需要重建提示快取;下一次請求可能較慢或成本較高。',
813
816
  modelSearchHint: '搜尋模型 / 服務商 / 連線 · ↑↓ 選擇 · Enter 確認 · Esc 取消',
814
817
  searchLabel: '搜尋',
815
818
  noMatchingModels: '沒有符合的模型',
@@ -0,0 +1,36 @@
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
+ export function stripUnfocusedCursorStyle(lines, focused) {
20
+ if (focused)
21
+ return lines;
22
+ // pi-tui 0.84.4 paints its cursor even when the editor is unfocused.
23
+ // Remove the reverse-video wrapper, keeping the captured text ($1):
24
+ //
25
+ // Input: \x1b[7mhello\x1b[0m
26
+ // └─────┘└───┘└─────┘
27
+ // reverse text reset
28
+ // remove keep remove
29
+ // $1
30
+ // Output: hello
31
+ //
32
+ // ([^\x1b]*) captures text without ESC, so the match cannot cross another
33
+ // ANSI sequence. Other text using this same wrapper would also lose its
34
+ // reverse styling. Recheck when changing themes or upgrading pi-tui.
35
+ return lines.map((line) => line.replace(/\x1b\[7m([^\x1b]*)\x1b\[0m/gu, '$1'));
36
+ }
@@ -20,7 +20,7 @@ import { createHash, randomUUID } from 'node:crypto';
20
20
  import { MCP_CONFIG_VERSION, mcpConfigChangeRetiresCredentials, resolveMcpProtocolPreference, } from '@maka/core/mcp';
21
21
  import { createCredentialMcpOAuthStorage, McpClientManager } from '@maka/mcp';
22
22
  import { createFileCredentialStore } from '@maka/storage/credential-store';
23
- import { createMcpConfigStore, assertMcpEndpointPolicyOnChanges, McpConfigSourceError, normalizeMcpConfig, normalizeMcpImport, } from '@maka/storage/mcp-config-store';
23
+ import { AtomicFileWriteCommitUnknownError, createMcpConfigStore, assertMcpEndpointPolicyOnChanges, McpConfigSourceError, normalizeMcpConfig, normalizeMcpImport, } from '@maka/storage/mcp-config-store';
24
24
  import { createMcpCapabilityProvider } from './mcp-capability-provider.js';
25
25
  const RUNTIME_HOST_CREDENTIAL_ENV = 'MAKA_RUNTIME_HOST_ACCESS_CREDENTIAL';
26
26
  export function createTuiMcpController(input, overrides = {}) {
@@ -289,10 +289,45 @@ class TuiMcpControllerImpl {
289
289
  catch (error) {
290
290
  if (error instanceof TuiMcpMutationError)
291
291
  return error.result;
292
+ if (error instanceof AtomicFileWriteCommitUnknownError) {
293
+ // The transform has already published, including any credential
294
+ // retirement. Reload its authority; never replay those effects.
295
+ this.#preparedImport = undefined;
296
+ let reconciliationError;
297
+ if (!this.#closed) {
298
+ try {
299
+ ({ reconciliationError } = await this.#synchronizeCommittedConfig(await this.#deps.configStore.get()));
300
+ }
301
+ catch (failure) {
302
+ reconciliationError = failure;
303
+ this.#publicationSuppressed = false;
304
+ this.#snapshot = freezeSnapshot({
305
+ ...this.#snapshot,
306
+ configuration: 'out_of_sync',
307
+ servers: this.#snapshot.servers.map((server) => ({
308
+ ...server,
309
+ synchronized: false,
310
+ })),
311
+ });
312
+ this.#notify();
313
+ }
314
+ }
315
+ // Reconciliation does not establish the missing durability fence,
316
+ // and its own failure must not replace the original write error.
317
+ return {
318
+ status: 'failed',
319
+ reason: 'commit-unknown',
320
+ cause: error,
321
+ ...(reconciliationError === undefined ? {} : { reconciliationError }),
322
+ };
323
+ }
292
324
  return { status: 'failed', reason: 'persist-failed' };
293
325
  }
326
+ return (await this.#synchronizeCommittedConfig(committed)).result;
327
+ }
328
+ async #synchronizeCommittedConfig(committed) {
294
329
  if (this.#closed)
295
- return { status: 'failed', reason: 'closed' };
330
+ return { result: { status: 'failed', reason: 'closed' } };
296
331
  this.#preparedImport = undefined;
297
332
  this.#config = cloneConfig(committed);
298
333
  this.#updateSnapshot({ configuration: 'synchronizing' });
@@ -301,19 +336,19 @@ class TuiMcpControllerImpl {
301
336
  try {
302
337
  await this.#deps.manager.sync(committed);
303
338
  }
304
- catch {
339
+ catch (error) {
305
340
  this.#publicationSuppressed = false;
306
341
  this.#updateSnapshot({ configuration: 'out_of_sync' });
307
342
  this.#refreshManagerSnapshot();
308
343
  await this.#settlePublication();
309
- return { status: 'applied', effect: 'sync_failed' };
344
+ return { result: { status: 'applied', effect: 'sync_failed' }, reconciliationError: error };
310
345
  }
311
346
  this.#publicationSuppressed = false;
312
347
  if (this.#closed)
313
- return { status: 'failed', reason: 'closed' };
348
+ return { result: { status: 'failed', reason: 'closed' } };
314
349
  this.#updateSnapshot({ configuration: 'ready' });
315
350
  this.#refreshManagerSnapshot();
316
- return { status: 'applied', effect: await this.#settlePublication() };
351
+ return { result: { status: 'applied', effect: await this.#settlePublication() } };
317
352
  }
318
353
  #prepareMutation(current, action) {
319
354
  const servers = { ...current.mcpServers };
@@ -31,6 +31,7 @@
31
31
  * Phase 1 scope: types + small pure helpers only. No storage, runner,
32
32
  * projection, or ledger logic lives here. Those arrive in later nodes.
33
33
  */
34
+ import { isWorkHubActionReceipt } from './workhub-action-result.js';
34
35
  import { isModelRetryDecision } from './model-failure.js';
35
36
  import { isRuntimeHandoffPause, runtimeHandoffPause, } from './runtime-handoff.js';
36
37
  import { isMessageContent, normalizeMessageContent, } from './events.js';
@@ -229,6 +230,7 @@ const INVOCATION_ROOT_SHAPES = {
229
230
  };
230
231
  const RUNTIME_ACTIONS_SHAPE = defineObjectShape()([], [
231
232
  'handoffPause',
233
+ 'coordination',
232
234
  'stateDelta',
233
235
  'artifactDelta',
234
236
  'permissionRequest',
@@ -583,6 +585,7 @@ function isRuntimeEventActions(value) {
583
585
  return false;
584
586
  }
585
587
  return ((value.handoffPause === undefined || isRuntimeHandoffPause(value.handoffPause)) &&
588
+ (value.coordination === undefined || isWorkHubActionReceipt(value.coordination)) &&
586
589
  (value.stateDelta === undefined || isRecord(value.stateDelta)) &&
587
590
  (value.artifactDelta === undefined ||
588
591
  (isRecord(value.artifactDelta) &&
@@ -16,6 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
+ import { isWorkHubActionReceipt } from './workhub-action-result.js';
19
20
  import { MODEL_FAILURE_MESSAGE_MAX_BYTES, isModelRetryDecision, } from './model-failure.js';
20
21
  import { decodeMessageContent, TOOL_ACTIVITY_KINDS, } from './events.js';
21
22
  import { isPermissionMode, isToolCategory, } from './permission.js';
@@ -64,7 +65,11 @@ export function isTurnStatus(value) {
64
65
  // ============================================================================
65
66
  // Header (JSONL line 1)
66
67
  // ============================================================================
67
- export const SESSION_TOOL_PROFILES = ['headless-coding-v1', 'workhub-coordination-v1'];
68
+ export const SESSION_TOOL_PROFILES = [
69
+ 'headless-coding-v1',
70
+ 'workhub-coordination-v1',
71
+ 'workhub-coordination-v2',
72
+ ];
68
73
  export function isSessionToolProfile(value) {
69
74
  return typeof value === 'string' && SESSION_TOOL_PROFILES.includes(value);
70
75
  }
@@ -341,6 +346,7 @@ const USER_MESSAGE_SHAPE = defineObjectShape()(['type', 'id', 'turnId', 'ts', 't
341
346
  'quotes',
342
347
  'inlineReferences',
343
348
  'steeringEventId',
349
+ 'coordinationActionId',
344
350
  'origin',
345
351
  ]);
346
352
  const ASSISTANT_MESSAGE_SHAPE = defineObjectShape()(['type', 'id', 'turnId', 'ts', 'text', 'modelId'], ['thinking', 'contentOrder', 'providerOptions']);
@@ -418,7 +424,15 @@ const WORKHUB_DELEGATION_ASSIGNED_MESSAGE_SHAPE = defineObjectShape()([
418
424
  'targetTurnId',
419
425
  'targetMessageId',
420
426
  'targetSessionName',
421
- ], ['attachments', 'create', 'steered', 'replacesActionId', 'replacesDelegationId']);
427
+ ], [
428
+ 'attachments',
429
+ 'targetAttachments',
430
+ 'create',
431
+ 'steered',
432
+ 'replacesActionId',
433
+ 'replacesDelegationId',
434
+ 'delegationText',
435
+ ]);
422
436
  const WORKHUB_DELEGATION_REPLACEMENT_REQUESTED_MESSAGE_SHAPE = defineObjectShape()([
423
437
  'type',
424
438
  'id',
@@ -437,7 +451,7 @@ const WORKHUB_DELEGATION_REPLACEMENT_REQUESTED_MESSAGE_SHAPE = defineObjectShape
437
451
  'replacedTargetSessionId',
438
452
  'replacedTargetMessageId',
439
453
  'targetSessionName',
440
- ], ['attachments', 'create']);
454
+ ], ['attachments', 'create', 'delegationText']);
441
455
  const WORKHUB_DELEGATION_SUPERSEDED_MESSAGE_SHAPE = defineObjectShape()([
442
456
  'type',
443
457
  'id',
@@ -523,7 +537,10 @@ function decodeMessage(value, decodeToolResultContent) {
523
537
  case 'user':
524
538
  if (hasExactShape(message, USER_MESSAGE_SHAPE) &&
525
539
  hasMessageEnvelope(message, true) &&
526
- (message.origin === undefined || decodeTurnOrigin(message.origin) !== undefined)) {
540
+ (message.origin === undefined || decodeTurnOrigin(message.origin) !== undefined) &&
541
+ (message.coordinationActionId === undefined ||
542
+ (typeof message.coordinationActionId === 'string' &&
543
+ message.coordinationActionId.length > 0))) {
527
544
  const { displayText, attachments, directoryReferences, quotes, inlineReferences, origin, ...envelope } = message;
528
545
  const decodedOrigin = origin === undefined ? undefined : decodeTurnOrigin(origin);
529
546
  try {
@@ -632,6 +649,11 @@ function decodeMessage(value, decodeToolResultContent) {
632
649
  throw new Error('Invalid stored message schema');
633
650
  }
634
651
  function isWorkHubCoordinationMessage(message) {
652
+ if (message.kind === 'action_receipt')
653
+ return (hasMessageEnvelope(message, true) &&
654
+ message.schemaVersion === 1 &&
655
+ Object.keys(message).every((k) => ['type', 'kind', 'schemaVersion', 'id', 'turnId', 'ts', 'receipt'].includes(k)) &&
656
+ isWorkHubActionReceipt(message.receipt));
635
657
  if (message.kind === 'delegation_stop_requested') {
636
658
  return (hasMessageEnvelope(message, true) &&
637
659
  hasExactShape(message, WORKHUB_DELEGATION_STOP_REQUESTED_MESSAGE_SHAPE) &&
@@ -708,6 +730,8 @@ function isWorkHubCoordinationMessage(message) {
708
730
  typeof message.userText === 'string' &&
709
731
  message.userText.trim().length > 0 &&
710
732
  isWorkHubMessageAttachments(message.attachments) &&
733
+ (message.delegationText === undefined ||
734
+ (typeof message.delegationText === 'string' && message.delegationText.trim().length > 0)) &&
711
735
  ((message.disposition === 'delegate_existing' && message.create === undefined) ||
712
736
  (message.disposition === 'create_new' && isWorkHubDelegationCreateSpec(message.create))) &&
713
737
  (message.disposition === 'delegate_existing' || message.disposition === 'create_new');
@@ -729,6 +753,7 @@ function isWorkHubCoordinationMessage(message) {
729
753
  }
730
754
  return (message.kind === 'delegation_assigned' &&
731
755
  hasExactShape(message, WORKHUB_DELEGATION_ASSIGNED_MESSAGE_SHAPE) &&
756
+ isWorkHubMessageAttachments(message.targetAttachments) &&
732
757
  typeof message.delegationId === 'string' &&
733
758
  typeof message.targetTurnId === 'string' &&
734
759
  typeof message.targetMessageId === 'string' &&
@@ -0,0 +1,68 @@
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
+ import { isRecord } from './record-schema.js';
20
+ export function isWorkHubActionReceipt(value) {
21
+ if (!isRecord(value) ||
22
+ Object.keys(value).some((k) => !['actionId', 'userText', 'result', 'clarification'].includes(k)) ||
23
+ typeof value.actionId !== 'string' ||
24
+ !value.actionId ||
25
+ typeof value.userText !== 'string' ||
26
+ !value.userText.trim() ||
27
+ (value.clarification !== undefined && typeof value.clarification !== 'string'))
28
+ return false;
29
+ return isWorkHubActionResult(value.result);
30
+ }
31
+ /** Shared closed result contract for Runtime receipts and Host protocol replies. */
32
+ export function isWorkHubActionResult(r) {
33
+ if (!isRecord(r))
34
+ return false;
35
+ const text = (k) => typeof r[k] === 'string' && /^[A-Za-z0-9_-]{1,128}$/u.test(r[k]);
36
+ const keys = (allowed) => Object.keys(r).every((k) => allowed.includes(k));
37
+ if (r.disposition === 'answer_here' || r.disposition === 'clarify')
38
+ return keys(['disposition', 'coordinationTurnId']) && text('coordinationTurnId');
39
+ if (r.disposition === 'delegate_existing' ||
40
+ r.disposition === 'create_new' ||
41
+ r.disposition === 'replace')
42
+ return (keys([
43
+ 'disposition',
44
+ 'targetSessionId',
45
+ 'targetTurnId',
46
+ 'steered',
47
+ ...(r.disposition === 'replace' ? ['replacementDisposition'] : []),
48
+ ]) &&
49
+ text('targetSessionId') &&
50
+ text('targetTurnId') &&
51
+ (r.steered === undefined || r.steered === true) &&
52
+ (r.disposition !== 'replace' ||
53
+ r.replacementDisposition === 'delegate_existing' ||
54
+ r.replacementDisposition === 'create_new'));
55
+ if (r.disposition === 'stop_work' || r.disposition === 'resume_work')
56
+ return ((r.disposition === 'stop_work'
57
+ ? ((r.outcome !== 'stop_delivered' && r.outcome !== 'not_owned') ||
58
+ r.targetTurnId !== undefined) &&
59
+ (r.outcome !== 'cancelled_pending' || r.targetTurnId === undefined)
60
+ : (r.outcome === 'resume_started') === (r.targetTurnId !== undefined)) &&
61
+ keys(['disposition', 'outcome', 'targetSessionId', 'targetTurnId']) &&
62
+ text('targetSessionId') &&
63
+ (r.targetTurnId === undefined || text('targetTurnId')) &&
64
+ (r.disposition === 'stop_work'
65
+ ? ['cancelled_pending', 'stop_delivered', 'already_terminal', 'not_owned']
66
+ : ['resume_started', 'already_running']).includes(String(r.outcome)));
67
+ return false;
68
+ }
@@ -83,8 +83,6 @@
83
83
  "./deep-research-client-progress": "./dist/deep-research-client-progress.js",
84
84
  "./daily-review": "./dist/daily-review.js",
85
85
  "./work-board": "./dist/work-board.js",
86
- "./workhub-creation-intent": "./dist/workhub-creation-intent.js",
87
- "./workhub-session-resolver": "./dist/workhub-session-resolver.js",
88
86
  "./deep-research": "./dist/deep-research.js",
89
87
  "./session-start-mode": "./dist/session-start-mode.js",
90
88
  "./long-term-memory": "./dist/long-term-memory.js",
@@ -156,7 +154,8 @@
156
154
  "./ui-locale": "./dist/ui-locale.js",
157
155
  "./unified-diff": "./dist/unified-diff.js",
158
156
  "./dev-single-instance": "./dist/dev-single-instance.js",
159
- "./maka-wordmark": "./dist/maka-wordmark.js"
157
+ "./maka-wordmark": "./dist/maka-wordmark.js",
158
+ "./workhub-action-result": "./dist/workhub-action-result.js"
160
159
  },
161
160
  "dependencies": {
162
161
  "intl-messageformat": "11.2.14"
@@ -60,7 +60,7 @@ export const TOOLCHAIN_IDENTITIES = {
60
60
  'deepseek-harness': {
61
61
  root: '/opt/maka-deepseek-harness-toolchain',
62
62
  version: '0.1.0-rc.6',
63
- fingerprint: 'sha256:a0882b448718ddfb7b64e33a12369c92b0064baf8388fe08a8ff64fe3dd98896',
63
+ fingerprint: 'sha256:5314e15659d4c35381ce15ca4802933d61bc3af718d7eeb8260f4fa32ccf5860',
64
64
  },
65
65
  };
66
66
  // The identity table is the profile registry: a profile without a pinned