@kingsoftcloud/agentkit-harness-node 0.0.0-stage → 0.0.2

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 (151) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +1 -0
  3. package/README.md +124 -2
  4. package/THIRD_PARTY_NOTICES.md +66 -0
  5. package/ZCode-LICENSE +202 -0
  6. package/dist/index.mjs +4871 -0
  7. package/dist/native-command-plan.LICENSE.txt +221 -0
  8. package/dist/native-command-plan.json +16 -0
  9. package/dist/native-command-plan.mjs +8 -0
  10. package/dist/rpc-entry.mjs +5682 -0
  11. package/package.json +169 -4
  12. package/src/native/a2a-receipt-source.mjs +19 -0
  13. package/src/native/a2a-tools.mjs +517 -0
  14. package/src/native/activity-status.mjs +30 -0
  15. package/src/native/artifact-codec.mjs +481 -0
  16. package/src/native/artifact-store.mjs +1162 -0
  17. package/src/native/assistant-stream.mjs +76 -0
  18. package/src/native/background-command-port.mjs +95 -0
  19. package/src/native/browser-action-observation.mjs +43 -0
  20. package/src/native/browser-availability.mjs +22 -0
  21. package/src/native/browser-canvas-metrics.mjs +71 -0
  22. package/src/native/browser-dom.mjs +193 -0
  23. package/src/native/browser-playwright.mjs +1249 -0
  24. package/src/native/browser-vision.mjs +369 -0
  25. package/src/native/browser-workspace-policy.mjs +22 -0
  26. package/src/native/browser-workspace-protocol.mjs +95 -0
  27. package/src/native/browser-workspace-server.mjs +164 -0
  28. package/src/native/browser-workspace.mjs +355 -0
  29. package/src/native/captured-source.mjs +3 -0
  30. package/src/native/child-budget-allocation.mjs +87 -0
  31. package/src/native/child-capabilities.mjs +35 -0
  32. package/src/native/child-command-resource.mjs +20 -0
  33. package/src/native/child-history.mjs +188 -0
  34. package/src/native/child-merge.mjs +98 -0
  35. package/src/native/child-model-surface.mjs +19 -0
  36. package/src/native/child-observation.mjs +100 -0
  37. package/src/native/child-owner.mjs +620 -0
  38. package/src/native/child-publication.mjs +76 -0
  39. package/src/native/child-readers.mjs +366 -0
  40. package/src/native/child-tools.mjs +46 -0
  41. package/src/native/child-workspace-binding.mjs +29 -0
  42. package/src/native/child-workspace-tools.mjs +1 -0
  43. package/src/native/child-workspaces.mjs +655 -0
  44. package/src/native/children.mjs +365 -0
  45. package/src/native/command-plan-worker.mjs +14 -0
  46. package/src/native/command-plan.mjs +31 -0
  47. package/src/native/command-progress.mjs +64 -0
  48. package/src/native/command-shell.mjs +9 -0
  49. package/src/native/command-workspace-observation.mjs +36 -0
  50. package/src/native/component-tools.mjs +280 -0
  51. package/src/native/context-counter.mjs +133 -0
  52. package/src/native/context-markers.mjs +51 -0
  53. package/src/native/context-profile.mjs +72 -0
  54. package/src/native/context-status.mjs +27 -0
  55. package/src/native/conversation-command-control.mjs +133 -0
  56. package/src/native/delivery-timeline.mjs +39 -0
  57. package/src/native/dsh-background-ownership.mjs +102 -0
  58. package/src/native/dsh-managed-ownership.mjs +23 -0
  59. package/src/native/dsh-turn-contract.mjs +3 -0
  60. package/src/native/dsh-turn-ownership.mjs +54 -0
  61. package/src/native/dsh-turn-runtime.mjs +498 -0
  62. package/src/native/e2b-background-supervisor.mjs +347 -0
  63. package/src/native/e2b-background.mjs +368 -0
  64. package/src/native/e2b-command-state.mjs +40 -0
  65. package/src/native/e2b-command-tools.mjs +486 -0
  66. package/src/native/e2b-sandbox-owner.mjs +227 -0
  67. package/src/native/hook-executor.mjs +249 -0
  68. package/src/native/instruction-assembly.mjs +60 -0
  69. package/src/native/instruction-context.mjs +54 -0
  70. package/src/native/local-background-supervisor.mjs +108 -0
  71. package/src/native/local-background.mjs +207 -0
  72. package/src/native/local-command-evidence.mjs +57 -0
  73. package/src/native/local-command-policy-worker.mjs +197 -0
  74. package/src/native/local-command-policy.mjs +46 -0
  75. package/src/native/local-command-state.mjs +177 -0
  76. package/src/native/local-command-tools.mjs +302 -0
  77. package/src/native/local-execution-profile.mjs +36 -0
  78. package/src/native/local-output-policy.mjs +92 -0
  79. package/src/native/local-process-bin/darwin-arm64/launcher +0 -0
  80. package/src/native/local-process-bin/darwin-x64/launcher +0 -0
  81. package/src/native/local-process-bin/linux-arm64/launcher +0 -0
  82. package/src/native/local-process-bin/linux-x64/launcher +0 -0
  83. package/src/native/local-process-launcher.c +183 -0
  84. package/src/native/local-process-licenses/GCC-COPYING.RUNTIME +72 -0
  85. package/src/native/local-process-licenses/GCC-COPYING3 +674 -0
  86. package/src/native/local-process-licenses/NOTICE +21 -0
  87. package/src/native/local-process-licenses/musl-COPYRIGHT +193 -0
  88. package/src/native/local-process-manifest.json +32 -0
  89. package/src/native/local-process.mjs +246 -0
  90. package/src/native/local-resource-profile.mjs +32 -0
  91. package/src/native/memory-candidate.mjs +121 -0
  92. package/src/native/memory-extraction-run.mjs +60 -0
  93. package/src/native/memory-extraction-tools.mjs +116 -0
  94. package/src/native/memory-recall.mjs +65 -0
  95. package/src/native/memory-store.mjs +334 -0
  96. package/src/native/memory-tools.mjs +329 -0
  97. package/src/native/model-anthropic-cache.mjs +49 -0
  98. package/src/native/model-anthropic-tool-search.mjs +85 -0
  99. package/src/native/model-anthropic.mjs +753 -0
  100. package/src/native/model-chat-usage.mjs +24 -0
  101. package/src/native/model-inline-reasoning.mjs +111 -0
  102. package/src/native/model-media.mjs +174 -0
  103. package/src/native/model-normalize.mjs +228 -0
  104. package/src/native/model-openai-chat.mjs +561 -0
  105. package/src/native/model-port.mjs +361 -0
  106. package/src/native/model-provider-compat.mjs +129 -0
  107. package/src/native/model-replay.mjs +153 -0
  108. package/src/native/model-request.mjs +15 -0
  109. package/src/native/model-responses.mjs +1205 -0
  110. package/src/native/model-sse.mjs +171 -0
  111. package/src/native/model-surface-host.mjs +27 -0
  112. package/src/native/model-tool-search-policy.mjs +16 -0
  113. package/src/native/model-transport-observation.mjs +27 -0
  114. package/src/native/owned-child.mjs +114 -0
  115. package/src/native/owned-command.mjs +106 -0
  116. package/src/native/python-syntax-worker.mjs +60 -0
  117. package/src/native/python-syntax.mjs +50 -0
  118. package/src/native/read-interruption.mjs +16 -0
  119. package/src/native/result-stream.mjs +63 -0
  120. package/src/native/ruff-ast.mjs +95 -0
  121. package/src/native/run-observation.mjs +128 -0
  122. package/src/native/session-delivery-journal.mjs +96 -0
  123. package/src/native/session-delivery.mjs +434 -0
  124. package/src/native/shell-analysis.mjs +50 -0
  125. package/src/native/skill-disclosure.mjs +404 -0
  126. package/src/native/skill-events.mjs +164 -0
  127. package/src/native/skill-tools.mjs +813 -0
  128. package/src/native/source-page.mjs +73 -0
  129. package/src/native/sqlite-session-inbox.mjs +42 -0
  130. package/src/native/sqlite-store.mjs +617 -0
  131. package/src/native/tool-authority.mjs +99 -0
  132. package/src/native/tool-guidance.mjs +12 -0
  133. package/src/native/tool-observation.mjs +75 -0
  134. package/src/native/tool-states.mjs +14 -0
  135. package/src/native/verification-authority.mjs +37 -0
  136. package/src/native/web-tools.mjs +643 -0
  137. package/src/native/workspace-checkpoints.mjs +312 -0
  138. package/src/native/workspace-context.mjs +61 -0
  139. package/src/native/workspace-display.mjs +39 -0
  140. package/src/native/workspace-edit-match.mjs +19 -0
  141. package/src/native/workspace-edit-worker.mjs +15 -0
  142. package/src/native/workspace-editing.mjs +447 -0
  143. package/src/native/workspace-image.mjs +72 -0
  144. package/src/native/workspace-instructions.mjs +107 -0
  145. package/src/native/workspace-mutation-lock.mjs +74 -0
  146. package/src/native/workspace-patch-worker.mjs +17 -0
  147. package/src/native/workspace-patch.mjs +17 -0
  148. package/src/native/workspace-paths.mjs +225 -0
  149. package/src/native/workspace-state.mjs +272 -0
  150. package/src/native/workspace-subject-observer.mjs +84 -0
  151. package/src/native/workspace-tools.mjs +1740 -0
@@ -0,0 +1,63 @@
1
+ import { FINAL_SUBMISSION_TOOL, RESULT_DELIVERY_POLICY } from '@kingsoftcloud/agentkit-harness-core';
2
+
3
+ /** Decode only a top-level answer string. Nested artifact strings never become prose.
4
+ * This is a linear, non-authoritative display projection; the complete call is
5
+ * independently parsed and validated before it can become a committed result. */
6
+ export function answerStringDecoder() {
7
+ let depth = 0, role = null, key = '', keyExpected = false, selected = null;
8
+ let escaped = false, unicode = null, invalid = false;
9
+ return delta => {
10
+ let output = '';
11
+ const character = value => { if (role === 'answer') output += value;else if (role === 'key') key += value; };
12
+ for (const ch of delta) {
13
+ if (invalid) break;
14
+ if (role !== null) {
15
+ if (unicode !== null) {
16
+ if (!/^[0-9a-f]$/i.test(ch)) { invalid = true;break; }
17
+ unicode += ch;
18
+ if (unicode.length === 4) { character(String.fromCharCode(parseInt(unicode, 16)));unicode = null; }
19
+ } else if (escaped) {
20
+ escaped = false;
21
+ if (ch === 'u') unicode = '';
22
+ else {
23
+ const escapes = { '"': '"', '\\': '\\', '/': '/', b: '\b', f: '\f', n: '\n', r: '\r', t: '\t' };
24
+ if (!Object.hasOwn(escapes, ch)) { invalid = true;break; }
25
+ character(escapes[ch]);
26
+ }
27
+ } else if (ch === '\\') escaped = true;
28
+ else if (ch === '"') { if (role === 'key') { selected = key;keyExpected = false; }role = null; }
29
+ else if (ch.charCodeAt(0) < 32) { invalid = true;break; }
30
+ else character(ch);
31
+ } else if (ch === '"') {
32
+ role = depth === 1 && keyExpected ? 'key' : depth === 1 && selected === 'answer' ? 'answer' : 'other';key = '';
33
+ } else if (ch === '{' || ch === '[') { depth++;if (depth === 1) keyExpected = true; }
34
+ else if (ch === '}' || ch === ']') depth--;
35
+ else if (ch === ',' && depth === 1) { keyExpected = true;selected = null; }
36
+ }
37
+ return output;
38
+ };
39
+ }
40
+
41
+ /** Suppress the internal envelope while preserving its text answer as live prose. */
42
+ export function finalResultDisplay(configuration) {
43
+ if (configuration?.deliveryScopePolicy !== RESULT_DELIVERY_POLICY) return record => [record];
44
+ const tools = new Map(), known = new Set(configuration.tools.map(tool => tool.name));
45
+ return record => {
46
+ const { event } = record;
47
+ if (event.kind !== 'tool_delta') return [record];
48
+ const index = event.metadata.index;
49
+ let state = tools.get(index);
50
+ if (!state) { state = { name: '', pending: [], decode: answerStringDecoder() };tools.set(index, state); }
51
+ if (event.metadata.name) state.name = event.metadata.name;
52
+ state.pending.push(record);
53
+ if (!state.name) return [];
54
+ if (state.name === FINAL_SUBMISSION_TOOL) {
55
+ return state.pending.splice(0).flatMap(item => {
56
+ const text = state.decode(item.event.text);
57
+ return text ? [{ time: item.time, event: { kind: 'text', text } }] : [];
58
+ });
59
+ }
60
+ // A partial or unknown name is not yet a displayable bound tool.
61
+ return known.has(state.name) ? state.pending.splice(0) : [];
62
+ };
63
+ }
@@ -0,0 +1,95 @@
1
+ // Decoder for the pinned Ruff WASM Rust Debug AST, not a Python parser.
2
+ // Python grammar and literal decoding happen in Rust. Unknown wire syntax
3
+ // fails closed; the caller checks the exact parser version in its worker.
4
+ export function decodeRuffAst(text) {
5
+ let at = 0;
6
+ const fail = () => { throw Error('unsupported_ruff_ast_encoding'); };
7
+ const space = () => { while (/\s/.test(text[at] ?? '') && at < text.length) at++; };
8
+ const take = char => { space(); if (text[at++] !== char) fail(); };
9
+ function string() {
10
+ take('"'); let result = '';
11
+ while (at < text.length) {
12
+ const char = text[at++];
13
+ if (char === '"') return result;
14
+ if (char !== '\\') { result += char; continue; }
15
+ const escaped = text[at++], simple = { '"': '"', '\\': '\\', n: '\n', r: '\r', t: '\t', '0': '\0' };
16
+ if (Object.hasOwn(simple, escaped)) { result += simple[escaped]; continue; }
17
+ const match = escaped === 'u' ? /^\{([a-fA-F0-9]{1,6})\}/.exec(text.slice(at))
18
+ : escaped === 'x' ? /^([a-fA-F0-9]{2})/.exec(text.slice(at)) : null;
19
+ if (!match) fail();
20
+ const point = parseInt(match[1], 16); if (point > 0x10ffff) fail();
21
+ result += String.fromCodePoint(point); at += match[0].length;
22
+ }
23
+ fail();
24
+ }
25
+ function list(end, depth) {
26
+ const items = []; space();
27
+ while (text[at] !== end) {
28
+ items.push(value(depth + 1)); space();
29
+ if (text[at] !== ',') break;
30
+ at++; space();
31
+ }
32
+ take(end); return items;
33
+ }
34
+ function value(depth) {
35
+ if (depth > 1024) fail(); space();
36
+ if (text[at] === '"') return string();
37
+ if (text[at] === '[') { at++; return list(']', depth); }
38
+ if (text[at] === '(') { at++; return { tag: 'tuple', items: list(')', depth) }; }
39
+ const numeric = /^[+-]?(?:\d+\.\.\d+|\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/.exec(text.slice(at));
40
+ if (numeric) {
41
+ at += numeric[0].length;
42
+ if (numeric[0].includes('..')) return { range: numeric[0].split('..').map(Number) };
43
+ return { numeric: numeric[0] }; // Preserve large values without rounding.
44
+ }
45
+ const named = /^[A-Za-z_][A-Za-z0-9_:]*/.exec(text.slice(at)); if (!named) fail();
46
+ const tag = named[0]; at += tag.length; space();
47
+ if (text[at] === '(') { at++; return { tag, items: list(')', depth) }; }
48
+ if (text[at] === '{') {
49
+ at++; const fields = Object.create(null); space();
50
+ while (text[at] !== '}') {
51
+ const key = /^[A-Za-z_][A-Za-z0-9_]*/.exec(text.slice(at)); if (!key || Object.hasOwn(fields, key[0])) fail();
52
+ at += key[0].length; take(':'); fields[key[0]] = value(depth + 1); space();
53
+ if (text[at] !== ',') break;
54
+ at++; space();
55
+ }
56
+ take('}'); return { tag, fields };
57
+ }
58
+ return tag === 'true' ? true : tag === 'false' ? false : tag === 'None' ? null : { tag };
59
+ }
60
+ const root = value(0); space(); if (at !== text.length || root?.tag !== 'Module') fail();
61
+ return root;
62
+ }
63
+
64
+ export function unwrapRuff(value) {
65
+ while (value?.items?.length === 1) value = value.items[0];
66
+ return value;
67
+ }
68
+ export function ruffIdentifier(value) {
69
+ const node = unwrapRuff(value);
70
+ return typeof node === 'string' ? node : node?.tag === 'Identifier' ? ruffIdentifier(node.fields.id) : null;
71
+ }
72
+ export function ruffString(value) {
73
+ const node = unwrapRuff(value);
74
+ if (node?.tag !== 'ExprStringLiteral') return null;
75
+ const content = unwrapRuff(node.fields.value?.fields?.inner);
76
+ return ['StringLiteral', 'ConcatenatedStringLiteral'].includes(content?.tag) && typeof content.fields?.value === 'string'
77
+ ? content.fields.value : null;
78
+ }
79
+
80
+ // Breadth first across AST nodes, ignoring Rust enum/option/debug wrappers.
81
+ // This is used for whole-module alias collection followed by call inspection.
82
+ export function ruffNodes(root) {
83
+ const ast = node => node?.fields && /^(Mod|Stmt|Expr|Alias$|Keyword$|Comprehension$|Parameters$|Parameter$|WithItem$|MatchCase$|ExceptHandler)/.test(node.tag);
84
+ const children = item => {
85
+ if (!item || typeof item !== 'object') return [];
86
+ if (ast(item)) return [item];
87
+ return Object.values(item).flatMap(child => children(child));
88
+ };
89
+ const queue = children(root), result = [];
90
+ for (let i = 0; i < queue.length; i++) {
91
+ const node = queue[i]; result.push(node);
92
+ for (const [key, child] of Object.entries(node.fields)) if (key !== 'node_index' && key !== 'range') queue.push(...children(child));
93
+ }
94
+ return result;
95
+ }
@@ -0,0 +1,128 @@
1
+ import { copy, reviewedBackgroundInterruption, reviewableBackgroundInterruption,
2
+ reviewedResourceInterruption, retirableBrowserResources } from '@kingsoftcloud/agentkit-harness-core';
3
+ import { projectedNativeToolStates } from './tool-states.mjs';
4
+ import { advanceNativeToolObservation, nativeObservedToolStates } from './tool-observation.mjs';
5
+ import { nativeContextStatus } from './context-status.mjs';
6
+ import { nativeActivityStatus } from './activity-status.mjs';
7
+ import { nativeA2aReceiptWaitSource } from './a2a-receipt-source.mjs';
8
+ import { canCloseNativeReadInterruption } from './read-interruption.mjs';
9
+ import { advanceNativeContextMarkerState, nativeContextMarkerFactsFromState } from './context-markers.mjs';
10
+
11
+ export const NATIVE_RUN_OBSERVATION = 'agentkit.native-run-observation/v1';
12
+ export const NATIVE_COMMIT_PROJECTION = 'agentkit.native-commit-projection/v2';
13
+
14
+ function contextProjection(previous, record) {
15
+ let lastPlan = previous?.lastPlan ?? null, lastAdmission = previous?.lastAdmission ?? null;
16
+ for (const event of record.events ?? []) if (event.kind === 'context.planned') {
17
+ lastPlan = { kind: event.kind, seq: event.seq, payload: { action: event.payload.action } };
18
+ if (event.payload.admission) lastAdmission = { kind: event.kind, seq: event.seq,
19
+ payload: { admission: event.payload.admission } };
20
+ }
21
+ return { lastPlan, lastAdmission, markers: advanceNativeContextMarkerState(previous?.markers, record) };
22
+ }
23
+
24
+ function observationHeader(s) {
25
+ const header = Object.fromEntries(['scope', 'runId', 'sessionId', 'turnId', 'buildId', 'parentRunId', 'taskChainId',
26
+ 'configDigest', 'phase', 'revision', 'eventSeq', 'usage', 'descendantUsage', 'outcome', 'reason', 'pendingIntent',
27
+ 'toolReconciliation'].filter(key => s[key] !== undefined).map(key => [key, s[key]]));
28
+ header.diagnostics = Object.fromEntries(['providerUsageIncomplete', 'descendantUsageIncomplete', 'lastModelFailure']
29
+ .filter(key => s.diagnostics[key] !== undefined).map(key => [key, s.diagnostics[key]]));
30
+ return header;
31
+ }
32
+
33
+ /** A control transaction changes no transcript, receipt or event. Its display
34
+ * consequence is derived from current authority, never used as execution input. */
35
+ export function advanceNativeRunControlObservation(previous, state, changes) {
36
+ const view = copy(previous);
37
+ if (changes.claim) {
38
+ view.snapshot = observationHeader(changes.claim.snapshot);
39
+ const intent = changes.claim.intent;
40
+ view.facts.pendingEffect = { operationKey: intent.operationKey, kind: intent.kind, status: intent.status,
41
+ claimedFence: intent.claimedFence ?? null, receiptPresent: state.pendingIntentHasReceipt,
42
+ calls: (intent.payload.calls ?? []).map(({ callId, name }) => ({ callId, name })) };
43
+ view.commitProjection.tools = advanceNativeToolObservation(view.commitProjection.tools, { intents: [intent] });
44
+ for (const mode of ['idle', 'driving']) view.facts.toolStates[mode] = projectedNativeToolStates(
45
+ view.commitProjection.tools, changes.claim.snapshot, { driving: mode === 'driving' });
46
+ }
47
+ view.projections = copy(changes.projections ?? state.projections);
48
+ if (changes.commands?.some(row => !row.consumed)) {
49
+ view.facts.controlHints.reviewBackground = false;
50
+ view.facts.controlHints.retireResources = false;
51
+ view.facts.backgroundReview = null;
52
+ view.facts.resourceReview = null;
53
+ }
54
+ view.lease = copy(state.lease); view.fence = state.fence;
55
+ return copy(view);
56
+ }
57
+
58
+ /** Materialize a display read model inside the same ledger transaction.
59
+ * No transcript, model replay, tool arguments/results, image bytes or ports.
60
+ * Control hints are for display only: every control re-reads its original ledger.
61
+ */
62
+ export function projectNativeRunObservation(record, tools = advanceNativeToolObservation(null, record)) {
63
+ const { snapshot: s, configuration: c } = record;
64
+ const header = observationHeader(s);
65
+ const receiptWait = nativeA2aReceiptWaitSource(record);
66
+ const pending = record.intents.find(i => i.operationKey === s.pendingIntent);
67
+ const resourceReviewed = reviewedResourceInterruption(record);
68
+ const backgroundReviewed = reviewedBackgroundInterruption(record);
69
+ const toolOutcomes = nativeObservedToolStates(tools, s).filter(call => ['uncertain', 'not_dispatched'].includes(call.status));
70
+ const facts = {
71
+ toolStates: { idle: projectedNativeToolStates(tools, s), driving: projectedNativeToolStates(tools, s, { driving: true }) },
72
+ contextStatus: { idle: nativeContextStatus(record), driving: nativeContextStatus(record, { driving: true }) },
73
+ activity: nativeActivityStatus(record),
74
+ acceptance: c.contract.acceptance.map(({ required, verifierRef }) => ({ required, verifierRef })),
75
+ toolNames: c.tools.map(tool => tool.name),
76
+ a2aTasks: record.intents.filter(i => i.kind === 'tool').flatMap(i => (i.payload.calls ?? [])
77
+ .filter(call => call.name === 'call_a2a_agent').map(call => ({ runId: s.runId, operationKey: i.operationKey, callId: call.callId }))),
78
+ uncertainCalls: toolOutcomes.filter(row => row.status === 'uncertain').map(({ callId }) => ({ callId })),
79
+ toolOutcomes,
80
+ receiptWait: receiptWait ? { operationKey: receiptWait.intent.operationKey, callId: receiptWait.call.callId } : null,
81
+ pendingEffect: pending ? { operationKey: pending.operationKey, kind: pending.kind, status: pending.status,
82
+ claimedFence: pending.claimedFence ?? null, receiptPresent: record.receipts.some(r => r.operationKey === pending.operationKey),
83
+ calls: (pending.payload.calls ?? []).map(({ callId, name }) => ({ callId, name })) } : null,
84
+ controlHints: {
85
+ closeReadInterruption: canCloseNativeReadInterruption(record),
86
+ reviewBackground: reviewableBackgroundInterruption(record),
87
+ retireResources: Boolean(retirableBrowserResources(record)),
88
+ repairWorkspace: c.tools.some(tool => tool.name === s.pendingTools[0]?.name && tool.workspaceRepairPolicy === 'preimage-compensation/v1'),
89
+ },
90
+ backgroundReview: backgroundReviewed ? record.backgroundInterruptionReview : null,
91
+ resourceReview: resourceReviewed ? { contract: 'agentkit.resource-retirement-review/v1',
92
+ commandId: record.resourceRetirementReview.commandId, eventSeq: record.resourceRetirementReview.eventSeq,
93
+ resourceKind: record.resourceRetirementReview.fence.resourceKind } : null,
94
+ };
95
+ return copy({ contract: NATIVE_RUN_OBSERVATION, snapshot: header, lease: record.lease, fence: record.fence,
96
+ projections: record.projections, facts,
97
+ commitProjection: { contract: NATIVE_COMMIT_PROJECTION, context: contextProjection(null, record), tools } });
98
+ }
99
+
100
+ /** Advance display facts from a validated append. These facts are never used
101
+ * as execution authority. Exceptional recovery keeps the complete-ledger path. */
102
+ export function advanceNativeRunCommitObservation(previous, state, changes) {
103
+ const record = { ...state, snapshot: changes.snapshot, events: changes.events,
104
+ intents: [...state.intents, ...changes.intents], receipts: changes.receipts,
105
+ commands: changes.commands, projections: previous.projections };
106
+ const context = contextProjection(previous.commitProjection.context, record);
107
+ const tools = advanceNativeToolObservation(previous.commitProjection.tools, record);
108
+ const view = projectNativeRunObservation(record, tools);
109
+ const merge = (old, next, key) => [...new Map([...old, ...next].map(item => [key(item), item])).values()];
110
+ view.facts.a2aTasks = merge(previous.facts.a2aTasks, view.facts.a2aTasks, row => row.callId);
111
+ const contextRecord = { ...record, events: [context.lastAdmission, context.lastPlan].filter(Boolean) };
112
+ for (const mode of ['idle', 'driving']) {
113
+ const options = { driving: mode === 'driving' };
114
+ view.facts.contextStatus[mode] = nativeContextStatus(contextRecord, options);
115
+ if (view.facts.contextStatus[mode]) view.facts.contextStatus[mode].markers =
116
+ nativeContextMarkerFactsFromState(context.markers, record.snapshot, options).map(fact => fact.marker);
117
+ }
118
+ const observed = new Set(changes.events.filter(event => event.kind === 'background.commands_observed')
119
+ .flatMap(event => (event.payload.commands ?? []).map(command => command.taskId)));
120
+ const terminals = new Map(previous.facts.activity.terminals.map(row => [row.id, row]));
121
+ view.facts.activity.terminals = view.facts.activity.terminals.map(row => {
122
+ const old = terminals.get(row.id);
123
+ return !old ? row : { ...row, title: row.title === row.id ? old.title : row.title,
124
+ state: observed.has(row.id) ? row.state : old.state };
125
+ });
126
+ view.commitProjection = { contract: NATIVE_COMMIT_PROJECTION, context, tools };
127
+ return view;
128
+ }
@@ -0,0 +1,96 @@
1
+ import { copy, canonical, terminal, RESULT_DELIVERY_POLICY, FINAL_SUBMISSION_TOOL } from '@kingsoftcloud/agentkit-harness-core';
2
+ import { nativeDeliveryClosure } from './delivery-timeline.mjs';
3
+
4
+ // Presentation evidence only. Neither this journal nor its cursor authorizes
5
+ // execution, recovery, retries, model history or tool-result reconciliation.
6
+ export const NATIVE_DELIVERY_JOURNAL = 'agentkit.native-delivery-journal/v1';
7
+ export const DELIVERY_SNAPSHOT_FIELDS = Object.freeze(['scope', 'runId', 'sessionId', 'turnId', 'buildId',
8
+ 'parentRunId', 'taskChainId', 'configDigest', 'revision', 'eventSeq', 'phase', 'reason', 'finalText', 'messages', 'backgroundNotification']);
9
+ const eventKinds = new Set(['model.completed', 'context.summary_completed', 'completion.delivery_review_completed',
10
+ 'model.stream_interrupted', 'output.restarted', 'tool.completed', 'tool.not_dispatched', 'context.planned',
11
+ 'plan.initialized', 'plan.mode_changed', 'todo.initialized', 'todo.updated', 'effect.prepared']);
12
+ const pick = (value, fields) => Object.fromEntries(fields.filter(key => Object.hasOwn(value, key)).map(key => [key, value[key]]));
13
+
14
+ export function deliveryJournalHead(snapshot) {
15
+ return { contract: NATIVE_DELIVERY_JOURNAL, ...pick(snapshot,
16
+ ['scope', 'runId', 'sessionId', 'configDigest', 'revision', 'eventSeq']) };
17
+ }
18
+ export function matchesDeliveryJournal(head, snapshot) {
19
+ return head?.contract === NATIVE_DELIVERY_JOURNAL && head.revision === snapshot.revision
20
+ && head.eventSeq === snapshot.eventSeq && head.configDigest === snapshot.configDigest
21
+ && head.runId === snapshot.runId && head.sessionId === snapshot.sessionId
22
+ && canonical(head.scope) === canonical(snapshot.scope);
23
+ }
24
+
25
+ function deliveryEvent(event) {
26
+ if (!eventKinds.has(event.kind) || event.kind === 'effect.prepared' && event.payload.kind !== 'input') return null;
27
+ const fields = event.kind === 'tool.completed' ? ['callId', 'status']
28
+ : event.kind === 'tool.not_dispatched' ? ['callId']
29
+ : event.kind === 'context.planned' ? ['action', 'compactions']
30
+ : event.kind === 'effect.prepared' ? ['kind', 'operationKey'] : null;
31
+ return fields ? { ...event, payload: pick(event.payload, fields) } : event;
32
+ }
33
+
34
+ /** Full reference projection and incremental SQL append share this boundary.
35
+ * `intents` must contain the intents referenced by the supplied receipts. */
36
+ export function sessionDeliveryEntries({ events = [], intents = [], receipts = [] }) {
37
+ const rows = [], byKey = new Map(intents.map(intent => [intent.operationKey, intent]));
38
+ const add = (collection, key, data) => rows.push({ collection, key: String(key), data: copy(data) });
39
+ for (const event of events) {
40
+ const projected = deliveryEvent(event);
41
+ if (projected) add('events', event.seq, projected);
42
+ if (['tool.completed', 'tool.not_dispatched'].includes(event.kind))
43
+ add('outcomes', event.payload.callId, event.kind === 'tool.not_dispatched' ? 'not_dispatched' : event.payload.status);
44
+ }
45
+ for (const intent of intents) {
46
+ if (intent.kind === 'input' && Number.isSafeInteger(intent.payload.messageCursor))
47
+ add('inputs', intent.operationKey, intent.payload.messageCursor);
48
+ if (intent.kind === 'tool') for (const [index, call] of (intent.payload.calls ?? []).entries())
49
+ add('tools', call.callId, { operationKey: intent.operationKey, index });
50
+ }
51
+ for (const receipt of receipts) {
52
+ const intent = byKey.get(receipt.operationKey);
53
+ if (!intent) continue;
54
+ if (receipt.status === 'not_dispatched' && intent.kind === 'tool')
55
+ for (const call of intent.payload.calls ?? []) add('outcomes', call.callId, 'not_dispatched');
56
+ if (receipt.status !== 'committed') continue;
57
+ const pairs = intent.kind === 'tool' ? (intent.payload.calls ?? []).map((call, index) => [call.callId, receipt.result.results?.[index]])
58
+ : ['tool_receipt', 'workspace_repair'].includes(intent.kind) ? [[intent.payload.callId, receipt.result.result]] : [];
59
+ for (const [callId, observation] of pairs) {
60
+ const meta = observation?.metadata?.nativeTaskDisplay;
61
+ if (observation?.status === 'ok' && meta?.contract === 'agentkit.workspace-display/v1' && meta.display?.kind === 'file_edit')
62
+ add('displays', callId, meta);
63
+ }
64
+ }
65
+ return rows;
66
+ }
67
+
68
+ /** Calls absent from the committed transcript still need an exact terminal
69
+ * closure. Resolve only their original receipts, never every historical body. */
70
+ export function pendingDeliveryCalls(snapshot, configuration) {
71
+ if (!terminal(snapshot.phase)) return [];
72
+ const completed = new Set(snapshot.messages.filter(message => message.role === 'tool').map(message => message.callId));
73
+ return [...new Set(snapshot.messages.flatMap(message => (message.toolCalls ?? [])
74
+ .filter(call => configuration.deliveryScopePolicy !== RESULT_DELIVERY_POLICY || call.name !== FINAL_SUBMISSION_TOOL).map(call => call.callId)))]
75
+ .filter(callId => !completed.has(callId));
76
+ }
77
+
78
+ export function deliveryStateFromEntries({ snapshot, configuration, projections }, entries, resolveClosure) {
79
+ const groups = Object.fromEntries(['events', 'inputs', 'tools', 'outcomes', 'displays'].map(key => [key, new Map()]));
80
+ for (const row of entries) {
81
+ // A definitive nondispatch fact has the same precedence as the full
82
+ // receipt scan, including after later reconciliation events.
83
+ if (row.collection === 'outcomes' && groups.outcomes.get(row.key) === 'not_dispatched') continue;
84
+ if (row.collection === 'tools' && groups.tools.has(row.key)) continue;
85
+ groups[row.collection].set(row.key, row.data);
86
+ }
87
+ return copy({ contract: NATIVE_DELIVERY_JOURNAL,
88
+ snapshot: pick(snapshot, DELIVERY_SNAPSHOT_FIELDS), configuration, projections,
89
+ events: [...groups.events.values()].sort((a, b) => a.seq - b.seq),
90
+ inputBoundaries: [...groups.inputs], toolOutcomes: [...groups.outcomes], displays: [...groups.displays],
91
+ closures: pendingDeliveryCalls(snapshot, configuration).map(callId => [callId, resolveClosure(callId, groups.tools.get(callId))]) });
92
+ }
93
+
94
+ export function projectNativeSessionDelivery(record) {
95
+ return deliveryStateFromEntries(record, sessionDeliveryEntries(record), callId => nativeDeliveryClosure(record, callId));
96
+ }