maka-agent 0.2.0-dev.76.20261009 → 0.2.0-dev.78.20261010
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.
- package/README.md +1 -1
- package/README.zh-CN.md +3 -3
- package/dist/pi-tui-pickers.js +5 -2
- package/dist/pi-tui-runner.js +211 -33
- package/dist/runtime-host-session-driver.js +91 -13
- package/dist/session-catalog-limits.js +22 -0
- package/dist/session-driver.js +10 -0
- package/dist/tui-copy-catalog.js +15 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/bot-chat-settings.js +3 -0
- package/node_modules/@maka/core/dist/bot-events.js +1 -8
- package/node_modules/@maka/core/dist/local-memory.js +38 -1
- package/node_modules/@maka/core/dist/scheduled-task.js +14 -15
- package/node_modules/@maka/core/dist/session.js +3 -0
- package/node_modules/@maka/core/dist/text-sanitize.js +8 -3
- package/node_modules/@maka/runtime/dist/agent-graph-supervisor-wake.js +115 -17
- package/node_modules/@maka/runtime/dist/agent-run.js +11 -0
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +11 -0
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +13 -5
- package/node_modules/@maka/runtime/dist/bots/proxied-fetch.js +29 -1
- package/node_modules/@maka/runtime/dist/computer-use-observation-text.js +22 -0
- package/node_modules/@maka/runtime/dist/conversation-copy-agent-output.js +227 -0
- package/node_modules/@maka/runtime/dist/conversation-copy.js +176 -46
- package/node_modules/@maka/runtime/dist/file-stable-write.js +15 -3
- package/node_modules/@maka/runtime/dist/filesystem-worker/resource-resolver.js +3 -1
- package/node_modules/@maka/runtime/dist/model-adapter.js +20 -1
- package/node_modules/@maka/runtime/dist/network/proxy-test.js +28 -1
- package/node_modules/@maka/runtime/dist/network/scoped-fetch-transport.js +13 -2
- package/node_modules/@maka/runtime/dist/owned-child-process.js +209 -0
- package/node_modules/@maka/runtime/dist/owned-process-main.js +183 -0
- package/node_modules/@maka/runtime/dist/pipe-process-driver.js +19 -25
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +216 -8
- package/node_modules/@maka/runtime/dist/sandbox-boundary-tool.js +12 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +156 -17
- package/node_modules/@maka/runtime/dist/shell-exec.js +18 -20
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +2 -1
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +279 -8
- package/node_modules/@maka/runtime/dist/stream-graph-schedule-reconcile.js +209 -37
- package/node_modules/@maka/runtime/dist/tool-availability.js +55 -18
- package/node_modules/@maka/runtime/dist/tool-call-snapshot.js +142 -0
- package/node_modules/@maka/runtime/dist/tool-runtime.js +27 -84
- package/node_modules/@maka/runtime/dist/workers/{filesystem-worker.js → filesystem-worker.mjs} +12 -1
- package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +30 -6
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +44 -13
- package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +6 -0
- package/node_modules/@maka/runtime-host/dist/client/wsl-environment.js +4 -0
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
- package/node_modules/@maka/runtime-host/dist/protocol/message.js +1 -13
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +46 -9
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -13
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +1 -13
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +4 -7
- package/node_modules/@maka/runtime-host/dist/server/archive-retention-coordinator.js +81 -105
- package/node_modules/@maka/runtime-host/dist/server/candidate.js +47 -40
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +87 -67
- package/node_modules/@maka/runtime-host/dist/server/execution-service.js +5 -1
- package/node_modules/@maka/runtime-host/dist/server/host-maintenance-guards.js +95 -0
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +18 -8
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +32 -3
- package/node_modules/@maka/runtime-host/dist/server/project-catalog-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +109 -8
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +20 -3
- package/node_modules/@maka/runtime-host/dist/server/session-background-activity.js +71 -0
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +8 -4
- package/node_modules/@maka/runtime-host/dist/server/session-interaction-activity.js +78 -0
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +56 -0
- package/node_modules/@maka/runtime-host/dist/server/session-revision-graph-references.js +16 -2
- package/node_modules/@maka/runtime-host/dist/server/skill-catalog-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +19 -24
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +74 -19
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-coordinator.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-runtime.js +6 -99
- package/node_modules/@maka/runtime-host/dist/server/workhub-target-execution-authority.js +14 -126
- package/node_modules/@maka/storage/dist/context-offload-store.js +8 -3
- package/node_modules/@maka/storage/dist/git-entry.js +142 -5
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +54 -19
- package/package.json +2 -2
|
@@ -366,8 +366,30 @@ function header(observation, elementCount, query, shownCount) {
|
|
|
366
366
|
if (observation.truncated === true) {
|
|
367
367
|
parts.push('truncated=true(the tree was cut short; an element you expect may exist but not be listed)');
|
|
368
368
|
}
|
|
369
|
+
const selectedText = selectedTextCaption(observation);
|
|
370
|
+
if (selectedText)
|
|
371
|
+
parts.push(selectedText);
|
|
369
372
|
return parts.join(' ');
|
|
370
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Window selection is application text. A password field that currently holds
|
|
376
|
+
* keys must not leak that text through this caption; otherwise truncate like
|
|
377
|
+
* `value` and keep the executor's own truncated flag.
|
|
378
|
+
*/
|
|
379
|
+
function selectedTextCaption(observation) {
|
|
380
|
+
const selected = observation.selectedText;
|
|
381
|
+
if (!selected)
|
|
382
|
+
return undefined;
|
|
383
|
+
const focused = observation.elements.find((element) => element.focused === true);
|
|
384
|
+
const role = `${focused?.role ?? ''} ${focused?.subrole ?? ''}`;
|
|
385
|
+
if (/secure/i.test(role)) {
|
|
386
|
+
return 'selected_text=withheld(secure_field)';
|
|
387
|
+
}
|
|
388
|
+
const shown = truncate(selected.text);
|
|
389
|
+
return selected.truncated === true
|
|
390
|
+
? `selected_text=${quote(shown)}(truncated=true)`
|
|
391
|
+
: `selected_text=${quote(shown)}`;
|
|
392
|
+
}
|
|
371
393
|
/**
|
|
372
394
|
* A subrole earns its place when it is telling the model something the rest of
|
|
373
395
|
* the line does not.
|
|
@@ -0,0 +1,227 @@
|
|
|
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, isStringArray } from '@maka/core/record-schema';
|
|
20
|
+
import { decodeAgentRunEvent } from '@maka/core/agent-run';
|
|
21
|
+
import { decodeRuntimeEvent, decodeRuntimeInvocationOpened, } from '@maka/core/runtime-event';
|
|
22
|
+
import { runtimeInvocationOutcome } from '@maka/core/runtime-invocation';
|
|
23
|
+
import { stableJsonStringify } from '@maka/core/tool-args-identity';
|
|
24
|
+
import { runtimeInvocationFailureClass } from './runtime-event-read-model.js';
|
|
25
|
+
export function isConversationCopyAgentOutputSnapshot(value) {
|
|
26
|
+
return (isRecord(value) &&
|
|
27
|
+
value.kind === 'maka.agent_output_snapshot' &&
|
|
28
|
+
isResultPayload(value) &&
|
|
29
|
+
Object.keys(value).every((key) => [
|
|
30
|
+
'kind',
|
|
31
|
+
'schemaVersion',
|
|
32
|
+
'status',
|
|
33
|
+
'text',
|
|
34
|
+
'textTruncated',
|
|
35
|
+
'artifactIds',
|
|
36
|
+
'omittedArtifactIds',
|
|
37
|
+
'failureClass',
|
|
38
|
+
].includes(key)));
|
|
39
|
+
}
|
|
40
|
+
function isResultPayload(value) {
|
|
41
|
+
return (value.schemaVersion === 1 &&
|
|
42
|
+
resultStatus(value.status) &&
|
|
43
|
+
isStringArray(value.artifactIds) &&
|
|
44
|
+
value.artifactIds.every(nonempty) &&
|
|
45
|
+
typeof value.textTruncated === 'boolean' &&
|
|
46
|
+
typeof value.omittedArtifactIds === 'number' &&
|
|
47
|
+
Number.isSafeInteger(value.omittedArtifactIds) &&
|
|
48
|
+
value.omittedArtifactIds >= 0 &&
|
|
49
|
+
(value.text === undefined || typeof value.text === 'string') &&
|
|
50
|
+
(value.failureClass === undefined || typeof value.failureClass === 'string'));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Recognize the defined result envelope, never arbitrary nested JSON ids.
|
|
54
|
+
* These are reference claims, not authority: the Host still checks them against
|
|
55
|
+
* the retained child's metadata, invocation ledger, and Artifact ownership.
|
|
56
|
+
*/
|
|
57
|
+
export function conversationCopyAgentOutput(value) {
|
|
58
|
+
if (!isRecord(value))
|
|
59
|
+
return undefined;
|
|
60
|
+
const { execution, invocation, budget } = value;
|
|
61
|
+
if (!isRecord(execution) ||
|
|
62
|
+
execution.kind !== 'child_session' ||
|
|
63
|
+
!nonempty(execution.sessionId) ||
|
|
64
|
+
!isRecord(invocation) ||
|
|
65
|
+
invocation.sessionId !== execution.sessionId ||
|
|
66
|
+
!nonempty(invocation.runId) ||
|
|
67
|
+
!nonempty(invocation.turnId) ||
|
|
68
|
+
!nonempty(invocation.invocationId) ||
|
|
69
|
+
(execution.currentRunId !== undefined && execution.currentRunId !== invocation.runId) ||
|
|
70
|
+
!isRecord(budget) ||
|
|
71
|
+
typeof budget.view !== 'string' ||
|
|
72
|
+
!['result', 'events', 'runtime_events', 'all'].includes(budget.view) ||
|
|
73
|
+
!Array.isArray(value.events) ||
|
|
74
|
+
!Array.isArray(value.runtimeEvents) ||
|
|
75
|
+
!Array.isArray(value.diagnostics) ||
|
|
76
|
+
!Array.isArray(value.artifacts))
|
|
77
|
+
return undefined;
|
|
78
|
+
try {
|
|
79
|
+
const opening = decodeRuntimeInvocationOpened(invocation.opening);
|
|
80
|
+
const terminal = invocation.terminalEvent === undefined
|
|
81
|
+
? undefined
|
|
82
|
+
: decodeRuntimeEvent(invocation.terminalEvent);
|
|
83
|
+
if (terminal &&
|
|
84
|
+
(terminal.sessionId !== execution.sessionId ||
|
|
85
|
+
terminal.runId !== invocation.runId ||
|
|
86
|
+
terminal.turnId !== invocation.turnId ||
|
|
87
|
+
terminal.invocationId !== invocation.invocationId ||
|
|
88
|
+
terminal.partial))
|
|
89
|
+
return undefined;
|
|
90
|
+
let result = value.result;
|
|
91
|
+
if (budget.view === 'result') {
|
|
92
|
+
if (value.events.length ||
|
|
93
|
+
value.runtimeEvents.length ||
|
|
94
|
+
value.diagnostics.length ||
|
|
95
|
+
value.artifacts.length)
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Diagnostic views have no committed-result field. Their invocation
|
|
100
|
+
// still identifies the observation, and their Artifact list can contain
|
|
101
|
+
// outputs omitted by a separately bounded view=result read.
|
|
102
|
+
if (result !== undefined ||
|
|
103
|
+
(budget.view === 'events' && value.runtimeEvents.length > 0) ||
|
|
104
|
+
(budget.view === 'runtime_events' && value.events.length > 0))
|
|
105
|
+
return undefined;
|
|
106
|
+
const sameRun = (event) => event.sessionId === execution.sessionId &&
|
|
107
|
+
event.runId === invocation.runId &&
|
|
108
|
+
event.turnId === invocation.turnId;
|
|
109
|
+
const events = value.events.map(decodeAgentRunEvent);
|
|
110
|
+
const runtimeEvents = value.runtimeEvents.map(decodeRuntimeEvent);
|
|
111
|
+
if (!events.every(sameRun) ||
|
|
112
|
+
!runtimeEvents.every((event) => sameRun(event) && event.invocationId === invocation.invocationId) ||
|
|
113
|
+
!value.diagnostics.every((item) => isRecord(item) &&
|
|
114
|
+
item.runId === invocation.runId &&
|
|
115
|
+
item.turnId === invocation.turnId &&
|
|
116
|
+
nonempty(item.code) &&
|
|
117
|
+
typeof item.message === 'string') ||
|
|
118
|
+
!value.artifacts.every((item) => isRecord(item) &&
|
|
119
|
+
nonempty(item.id) &&
|
|
120
|
+
item.sessionId === execution.sessionId &&
|
|
121
|
+
item.turnId === invocation.turnId))
|
|
122
|
+
return undefined;
|
|
123
|
+
const text = [
|
|
124
|
+
...runtimeEvents.flatMap(diagnosticRuntimeEventText),
|
|
125
|
+
...events.flatMap((event) => (event.message ? [event.message] : [])),
|
|
126
|
+
...value.diagnostics.map((item) => item.message),
|
|
127
|
+
].join('\n');
|
|
128
|
+
const failureClass = runtimeInvocationFailureClass({ terminalEvent: terminal });
|
|
129
|
+
result = {
|
|
130
|
+
schemaVersion: 1,
|
|
131
|
+
status: runtimeInvocationOutcome({ terminalEvent: terminal }) ?? 'running',
|
|
132
|
+
...(text ? { text } : {}),
|
|
133
|
+
// The bounded diagnostic view cannot establish a complete final text
|
|
134
|
+
// or count omitted Artifacts. Retain its readable excerpt and known ids.
|
|
135
|
+
textTruncated: true,
|
|
136
|
+
artifactIds: value.artifacts.map((item) => item.id),
|
|
137
|
+
omittedArtifactIds: 0,
|
|
138
|
+
...(failureClass ? { failureClass } : {}),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (!isRecord(result) || !isResultPayload(result))
|
|
142
|
+
return undefined;
|
|
143
|
+
if (terminal &&
|
|
144
|
+
(runtimeInvocationOutcome({ terminalEvent: terminal }) !== result.status ||
|
|
145
|
+
(result.terminalRuntimeEventId !== undefined &&
|
|
146
|
+
result.terminalRuntimeEventId !== terminal.id)))
|
|
147
|
+
return undefined;
|
|
148
|
+
if (!terminal && result.status !== 'running' && result.status !== 'waiting_for_user')
|
|
149
|
+
return undefined;
|
|
150
|
+
let graph;
|
|
151
|
+
if (result.graph !== undefined) {
|
|
152
|
+
if (!isRecord(result.graph) ||
|
|
153
|
+
!nonempty(result.graph.graphId) ||
|
|
154
|
+
!nonempty(result.graph.workId) ||
|
|
155
|
+
!nonempty(result.graph.operatorId))
|
|
156
|
+
return undefined;
|
|
157
|
+
graph = {
|
|
158
|
+
graphId: result.graph.graphId,
|
|
159
|
+
workId: result.graph.workId,
|
|
160
|
+
operatorId: result.graph.operatorId,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
reference: {
|
|
165
|
+
childSessionId: execution.sessionId,
|
|
166
|
+
runId: invocation.runId,
|
|
167
|
+
turnId: invocation.turnId,
|
|
168
|
+
status: result.status,
|
|
169
|
+
artifactIds: result.artifactIds,
|
|
170
|
+
...(result.failureClass ? { failureClass: result.failureClass } : {}),
|
|
171
|
+
...(opening.lineage?.resumedFromRunId
|
|
172
|
+
? { resumedFromRunId: opening.lineage.resumedFromRunId }
|
|
173
|
+
: {}),
|
|
174
|
+
...(terminal ? { terminalEventId: terminal.id } : {}),
|
|
175
|
+
...(graph ? { graph } : {}),
|
|
176
|
+
},
|
|
177
|
+
// An independent Side Conversation keeps a result snapshot, without the
|
|
178
|
+
// original execution, invocation, Graph, or event identities.
|
|
179
|
+
snapshot: {
|
|
180
|
+
kind: 'maka.agent_output_snapshot',
|
|
181
|
+
schemaVersion: 1,
|
|
182
|
+
status: result.status,
|
|
183
|
+
...(typeof result.text === 'string' ? { text: result.text } : {}),
|
|
184
|
+
textTruncated: result.textTruncated,
|
|
185
|
+
artifactIds: result.artifactIds,
|
|
186
|
+
omittedArtifactIds: result.omittedArtifactIds,
|
|
187
|
+
...(result.failureClass ? { failureClass: result.failureClass } : {}),
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** Keep tool evidence as static text without copying event or tool-call identities. */
|
|
196
|
+
function diagnosticRuntimeEventText(event) {
|
|
197
|
+
if (event.partial)
|
|
198
|
+
return [];
|
|
199
|
+
const content = event.content;
|
|
200
|
+
switch (content?.kind) {
|
|
201
|
+
case 'text':
|
|
202
|
+
return [content.text];
|
|
203
|
+
case 'function_call':
|
|
204
|
+
return [stableJsonStringify({ kind: content.kind, name: content.name, args: content.args })];
|
|
205
|
+
case 'function_response':
|
|
206
|
+
return [
|
|
207
|
+
stableJsonStringify({
|
|
208
|
+
kind: content.kind,
|
|
209
|
+
name: content.name,
|
|
210
|
+
result: content.result,
|
|
211
|
+
...(content.isError !== undefined ? { isError: content.isError } : {}),
|
|
212
|
+
}),
|
|
213
|
+
];
|
|
214
|
+
default:
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function nonempty(value) {
|
|
219
|
+
return typeof value === 'string' && value.length > 0;
|
|
220
|
+
}
|
|
221
|
+
function resultStatus(value) {
|
|
222
|
+
return (value === 'completed' ||
|
|
223
|
+
value === 'failed' ||
|
|
224
|
+
value === 'cancelled' ||
|
|
225
|
+
value === 'running' ||
|
|
226
|
+
value === 'waiting_for_user');
|
|
227
|
+
}
|
|
@@ -40,6 +40,8 @@ import { baseToolResultProjection, decodeLedgerTransition, reduceEffectiveModelP
|
|
|
40
40
|
import { archivedToolResultProjection } from './tool-result-archive-transition.js';
|
|
41
41
|
import { serializeToolResultProjectionV1 } from './tool-result-archive-encoding.js';
|
|
42
42
|
import { createHash } from 'node:crypto';
|
|
43
|
+
import { encodeCanonicalRuntimeEvent } from '@maka/core/canonical-runtime-event';
|
|
44
|
+
import { conversationCopyAgentOutput, isConversationCopyAgentOutputSnapshot, } from './conversation-copy-agent-output.js';
|
|
43
45
|
import { readToolResultPage, readableToolResult, resolveReadInput, READ_PAGE_MAX_BYTES, } from './read-page.js';
|
|
44
46
|
/** Walks every typed StorageRef site reached by conversation-copy rewriting. */
|
|
45
47
|
function collectConversationCopyStorageRefs(input) {
|
|
@@ -52,7 +54,7 @@ function collectConversationCopyStorageRefs(input) {
|
|
|
52
54
|
if (isArchivedToolResultPlaceholder(value))
|
|
53
55
|
return;
|
|
54
56
|
try {
|
|
55
|
-
addContent(
|
|
57
|
+
addContent(decodeConversationCopyToolResult(value));
|
|
56
58
|
}
|
|
57
59
|
catch {
|
|
58
60
|
// Opaque tool results carry no typed StorageRef.
|
|
@@ -141,7 +143,7 @@ export function rewriteConversationCopyMessage(message, references) {
|
|
|
141
143
|
if (message.type === 'tool_result') {
|
|
142
144
|
return {
|
|
143
145
|
...message,
|
|
144
|
-
content: rewriteToolResultContent(message.content, references),
|
|
146
|
+
content: rewriteToolResultContent(message.content, references, references.toolNamesByCall?.get(conversationCopyToolCallKey(message.turnId, message.toolUseId))),
|
|
145
147
|
};
|
|
146
148
|
}
|
|
147
149
|
if (message.type === 'token_usage' && message.providerRequestTraceId) {
|
|
@@ -153,7 +155,8 @@ export function rewriteConversationCopyMessage(message, references) {
|
|
|
153
155
|
return message;
|
|
154
156
|
}
|
|
155
157
|
function rewriteAttachmentResourceRefs(text, artifactIds) {
|
|
156
|
-
|
|
158
|
+
// Snapshot tool evidence can contain JSON-escaped quotes and newlines.
|
|
159
|
+
return text.replace(/maka:\/\/runtime\/attachments\/[^\\\s)\]}>`'",;:!]+/g, (candidate) => {
|
|
157
160
|
const parsed = parseAttachmentResourceRef(candidate);
|
|
158
161
|
const artifactId = parsed ? artifactIds.get(parsed.artifactId) : undefined;
|
|
159
162
|
return artifactId ? `maka://runtime/attachments/${artifactId}` : candidate;
|
|
@@ -320,6 +323,10 @@ export async function cloneConversationRuntimeLedger(input) {
|
|
|
320
323
|
const operationIds = toolOperationIdMap(flattenedPlans, targetInvocationIds);
|
|
321
324
|
const references = {
|
|
322
325
|
...input.referenceMap,
|
|
326
|
+
toolNamesByCall: conversationCopyToolNames({
|
|
327
|
+
messages: input.copiedMessages,
|
|
328
|
+
runtimeEvents: flattenedPlans.flatMap((plan) => plan.events),
|
|
329
|
+
}),
|
|
323
330
|
runIds,
|
|
324
331
|
invocationIds,
|
|
325
332
|
operationIds,
|
|
@@ -589,6 +596,14 @@ function rewriteReadInput(input, references, clonedEvents) {
|
|
|
589
596
|
: rewriteLedgerArchiveText(rewriteAttachmentResourceRefs(original, references.artifactIds), references);
|
|
590
597
|
if (!input.path.startsWith('maka://read/'))
|
|
591
598
|
return { ...input, path };
|
|
599
|
+
if (path !== original &&
|
|
600
|
+
!eventId &&
|
|
601
|
+
references.ledgerArchives?.has(original) &&
|
|
602
|
+
parseToolResultArchiveResourceRef(original)?.storage !== 'ledger') {
|
|
603
|
+
// A retired v1 body has become a rewritten ledger snapshot. Its previous
|
|
604
|
+
// byte offset and digest cannot address the new resource.
|
|
605
|
+
return { path };
|
|
606
|
+
}
|
|
592
607
|
const url = new URL(input.path);
|
|
593
608
|
if (eventId && path !== original) {
|
|
594
609
|
const copied = clonedEvents.get(eventId);
|
|
@@ -674,7 +689,21 @@ async function loadConversationCopyRunEvents(sourceRuns, sourceEvents, copyTurnI
|
|
|
674
689
|
];
|
|
675
690
|
}));
|
|
676
691
|
}
|
|
677
|
-
|
|
692
|
+
// Legacy archive bodies and provider projections can store the raw result;
|
|
693
|
+
// RuntimeEvents and transcript messages normally carry the JSON wrapper.
|
|
694
|
+
function decodeConversationCopyToolResult(value, toolName) {
|
|
695
|
+
if (toolName === 'agent_output' &&
|
|
696
|
+
(conversationCopyAgentOutput(value) || isConversationCopyAgentOutputSnapshot(value)))
|
|
697
|
+
return { kind: 'json', value };
|
|
698
|
+
return decodePersistedToolResultContent(markPersisted(value));
|
|
699
|
+
}
|
|
700
|
+
export function conversationCopyLinkedChildReferences(content, toolName) {
|
|
701
|
+
if (content.kind === 'json') {
|
|
702
|
+
if (toolName !== 'agent_output')
|
|
703
|
+
return [];
|
|
704
|
+
const output = conversationCopyAgentOutput(content.value);
|
|
705
|
+
return output ? [output.reference] : [];
|
|
706
|
+
}
|
|
678
707
|
if (content.kind === 'subagent') {
|
|
679
708
|
if (!content.childSessionId)
|
|
680
709
|
return [];
|
|
@@ -706,27 +735,56 @@ export function conversationCopyLinkedChildReferences(content) {
|
|
|
706
735
|
: []);
|
|
707
736
|
}
|
|
708
737
|
export function collectConversationCopyLinkedChildReferences(input) {
|
|
709
|
-
|
|
710
|
-
|
|
738
|
+
return conversationCopyToolResults(input).flatMap(({ content, toolName }) => conversationCopyLinkedChildReferences(content, toolName));
|
|
739
|
+
}
|
|
740
|
+
/** Pair transcript results with call metadata, including historical ledger-only calls. */
|
|
741
|
+
function conversationCopyToolNames(input) {
|
|
742
|
+
const names = new Map();
|
|
743
|
+
const add = (turnId, callId, name) => {
|
|
744
|
+
const key = conversationCopyToolCallKey(turnId, callId);
|
|
745
|
+
names.set(key, names.has(key) && names.get(key) !== name ? undefined : name);
|
|
746
|
+
};
|
|
747
|
+
for (const message of input.messages) {
|
|
748
|
+
if (message.type === 'tool_call')
|
|
749
|
+
add(message.turnId, message.id, message.toolName);
|
|
750
|
+
}
|
|
751
|
+
for (const event of input.runtimeEvents) {
|
|
752
|
+
if (event.content?.kind === 'function_call' || event.content?.kind === 'function_response') {
|
|
753
|
+
add(event.turnId, event.content.id, event.content.name);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return names;
|
|
757
|
+
}
|
|
758
|
+
function conversationCopyToolCallKey(turnId, callId) {
|
|
759
|
+
return JSON.stringify([turnId, callId]);
|
|
760
|
+
}
|
|
761
|
+
function conversationCopyToolResults(input) {
|
|
762
|
+
const results = [];
|
|
763
|
+
const toolNames = conversationCopyToolNames(input);
|
|
764
|
+
const add = (value, toolName) => {
|
|
711
765
|
if (isArchivedToolResultPlaceholder(value))
|
|
712
766
|
return;
|
|
713
767
|
try {
|
|
714
|
-
|
|
768
|
+
results.push({ content: decodeConversationCopyToolResult(value, toolName), toolName });
|
|
715
769
|
}
|
|
716
770
|
catch {
|
|
717
|
-
// Opaque tool results have no typed
|
|
771
|
+
// Opaque tool results have no typed conversation-copy references.
|
|
718
772
|
}
|
|
719
773
|
};
|
|
720
774
|
for (const message of input.messages) {
|
|
721
775
|
if (message.type === 'tool_result') {
|
|
722
|
-
|
|
776
|
+
results.push({
|
|
777
|
+
content: message.content,
|
|
778
|
+
toolName: toolNames.get(conversationCopyToolCallKey(message.turnId, message.toolUseId)),
|
|
779
|
+
});
|
|
723
780
|
}
|
|
724
781
|
}
|
|
725
782
|
for (const event of input.runtimeEvents) {
|
|
726
|
-
if (event.content?.kind === 'function_response')
|
|
727
|
-
add(event.content.result);
|
|
783
|
+
if (event.content?.kind === 'function_response') {
|
|
784
|
+
add(event.content.result, toolNames.get(conversationCopyToolCallKey(event.turnId, event.content.id)));
|
|
785
|
+
}
|
|
728
786
|
}
|
|
729
|
-
return
|
|
787
|
+
return results;
|
|
730
788
|
}
|
|
731
789
|
/**
|
|
732
790
|
* Collects the `relativePath` of every `session_file` StorageRef that the copied
|
|
@@ -737,6 +795,8 @@ export function collectConversationCopyLinkedChildReferences(input) {
|
|
|
737
795
|
* their refs resolve in `rewriteStorageRef`. Walks exactly the ref sites reached
|
|
738
796
|
* by `rewriteStorageRef`: user-message attachments, tool_result image refs, text
|
|
739
797
|
* runtime-event attachments, and function_response / archived tool-result images.
|
|
798
|
+
* Static agent_output snapshots also own their Artifacts; those can retain a
|
|
799
|
+
* child Turn id outside the copied parent's turn closure and need explicit inclusion.
|
|
740
800
|
*/
|
|
741
801
|
export function collectConversationCopySessionFileRefs(input) {
|
|
742
802
|
const refs = new Set();
|
|
@@ -745,6 +805,14 @@ export function collectConversationCopySessionFileRefs(input) {
|
|
|
745
805
|
refs.add(ref.relativePath);
|
|
746
806
|
}
|
|
747
807
|
}
|
|
808
|
+
for (const { content, toolName } of conversationCopyToolResults(input)) {
|
|
809
|
+
if (toolName === 'agent_output' &&
|
|
810
|
+
content.kind === 'json' &&
|
|
811
|
+
isConversationCopyAgentOutputSnapshot(content.value)) {
|
|
812
|
+
for (const artifactId of content.value.artifactIds)
|
|
813
|
+
refs.add(artifactId);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
748
816
|
return refs;
|
|
749
817
|
}
|
|
750
818
|
function cloneAgentRunEvent(event, ids, references, sourceCompactableEvents, clonedRuntimeEvents, checkpointIds, providerTraceIds, logicalCallIds, clonedTransitions) {
|
|
@@ -830,10 +898,9 @@ function cloneAgentRunEvent(event, ids, references, sourceCompactableEvents, clo
|
|
|
830
898
|
}
|
|
831
899
|
else if (event.type === MODEL_PROJECTION_TRANSITION_EVENT_TYPE) {
|
|
832
900
|
const cloned = clonedTransitions.get(event);
|
|
833
|
-
//
|
|
834
|
-
//
|
|
835
|
-
//
|
|
836
|
-
// cannot bring replaced content back.
|
|
901
|
+
// A transition is omitted only when its target was excluded or its archive
|
|
902
|
+
// wrapper was collapsed into an already copied archive. Neither case can
|
|
903
|
+
// bring replaced content back into the model-visible history.
|
|
837
904
|
if (!cloned)
|
|
838
905
|
return null;
|
|
839
906
|
data = { ...event.data, transition: cloned, runtimeEventId: cloned.target.runtimeEventId };
|
|
@@ -873,37 +940,57 @@ function cloneModelProjectionTransition(event, references, clonedRuntimeEvents,
|
|
|
873
940
|
}
|
|
874
941
|
const placeholder = rawPlaceholder;
|
|
875
942
|
const existing = transitionState.get(clonedTarget.id);
|
|
876
|
-
|
|
943
|
+
// RuntimeEvent storage canonicalizes JSON key order. Hash the bytes a later
|
|
944
|
+
// archive read will reconstruct, including newly built snapshot projections.
|
|
945
|
+
const sourceProjection = existing?.projection ??
|
|
946
|
+
baseToolResultProjection(encodeCanonicalRuntimeEvent(clonedTarget).event);
|
|
877
947
|
if (!sourceProjection) {
|
|
878
948
|
throw new Error(`Cannot copy model projection transition ${event.id} onto its target`);
|
|
879
949
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
950
|
+
const previousTransitionId = source.previousTransitionId
|
|
951
|
+
? requiredMappedId(transitionIds, source.previousTransitionId, 'model projection transition')
|
|
952
|
+
: undefined;
|
|
953
|
+
// Persisted placeholders can use a public event address or an exact legacy
|
|
954
|
+
// ledger identity. Both must resolve to the same rebuilt archive on recopy.
|
|
955
|
+
const sourceRefs = new Set([
|
|
956
|
+
buildToolResultArchiveResourceRef(placeholder),
|
|
957
|
+
...(placeholder.resourceRef ? [placeholder.resourceRef] : []),
|
|
958
|
+
]);
|
|
959
|
+
const previousPlaceholder = existing?.projection.kind === 'json' ? existing.projection.value : undefined;
|
|
960
|
+
if (existing &&
|
|
961
|
+
isArchivedToolResultPlaceholder(previousPlaceholder) &&
|
|
962
|
+
previousPlaceholder.rewriteVersion === 2) {
|
|
963
|
+
if (previousTransitionId !== existing.transitionId)
|
|
964
|
+
throw new Error(`Cannot collapse disconnected archive transition ${event.id}`);
|
|
965
|
+
// Public Read addresses identify an event, not a transition. Archiving
|
|
966
|
+
// this placeholder again would make that address return itself. Keep the
|
|
967
|
+
// first archive of the copied body and alias every later wrapper to it.
|
|
968
|
+
for (const sourceRef of sourceRefs)
|
|
969
|
+
references.ledgerArchives?.set(sourceRef, previousPlaceholder);
|
|
970
|
+
transitionIds.set(source.transitionId, existing.transitionId);
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
if (clonedTarget.content?.kind === 'function_response' && !clonedTarget.content.modelProjection)
|
|
974
|
+
clonedTarget.content.modelProjection = sourceProjection;
|
|
975
|
+
// Rebuild every archive over the copied ledger, including legacy v1 bodies.
|
|
883
976
|
const serialized = serializeToolResultProjectionV1(sourceProjection);
|
|
884
977
|
const rewritten = buildLedgerArchivedToolResultPlaceholder({
|
|
885
978
|
storage: 'ledger',
|
|
886
979
|
runtimeEventId: clonedTarget.id,
|
|
887
980
|
toolCallId: placeholder.toolCallId,
|
|
888
981
|
toolName: placeholder.toolName,
|
|
982
|
+
originalEstimatedTokens: placeholder.originalEstimatedTokens,
|
|
983
|
+
reason: placeholder.reason,
|
|
889
984
|
sourceProjectionDigest: durableToolResultProjectionDigest(sourceProjection),
|
|
890
985
|
bodySha256: createHash('sha256').update(serialized).digest('hex'),
|
|
891
986
|
originalBytes: Buffer.byteLength(serialized),
|
|
892
|
-
|
|
893
|
-
reason: placeholder.reason,
|
|
894
|
-
...(source.previousTransitionId
|
|
895
|
-
? {
|
|
896
|
-
previousTransitionId: requiredMappedId(transitionIds, source.previousTransitionId, 'model projection transition'),
|
|
897
|
-
}
|
|
898
|
-
: {}),
|
|
987
|
+
...(previousTransitionId ? { previousTransitionId } : {}),
|
|
899
988
|
});
|
|
900
989
|
if (placeholder.page) {
|
|
901
990
|
rewritten.page = readToolResultPage(serialized, { path: rewritten.resourceRef }, READ_PAGE_MAX_BYTES - Buffer.byteLength(JSON.stringify(rewritten)) - 32);
|
|
902
991
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
references.ledgerArchives?.set(placeholder.resourceRef ?? buildToolResultArchiveResourceRef(placeholder), rewritten);
|
|
906
|
-
}
|
|
992
|
+
for (const sourceRef of sourceRefs)
|
|
993
|
+
references.ledgerArchives?.set(sourceRef, rewritten);
|
|
907
994
|
const transition = buildModelProjectionTransition({
|
|
908
995
|
sessionId: references.targetSessionId,
|
|
909
996
|
target: {
|
|
@@ -917,11 +1004,7 @@ function cloneModelProjectionTransition(event, references, clonedRuntimeEvents,
|
|
|
917
1004
|
// The applied chain is copied in fold order, so a predecessor is always
|
|
918
1005
|
// rebuilt before its successor. An unmapped one means the chain broke, and
|
|
919
1006
|
// rooting the successor instead would change what the fold decides.
|
|
920
|
-
...(
|
|
921
|
-
? {
|
|
922
|
-
previousTransitionId: requiredMappedId(transitionIds, source.previousTransitionId, 'model projection transition'),
|
|
923
|
-
}
|
|
924
|
-
: {}),
|
|
1007
|
+
...(previousTransitionId ? { previousTransitionId } : {}),
|
|
925
1008
|
now: source.createdAt,
|
|
926
1009
|
});
|
|
927
1010
|
transitionIds.set(source.transitionId, transition.transitionId);
|
|
@@ -1135,6 +1218,9 @@ function rewriteInvocationOpening(opening, references) {
|
|
|
1135
1218
|
};
|
|
1136
1219
|
}
|
|
1137
1220
|
function rewriteRuntimeEventReferences(event, references) {
|
|
1221
|
+
const toolName = event.content?.kind === 'function_response'
|
|
1222
|
+
? references.toolNamesByCall?.get(conversationCopyToolCallKey(event.turnId, event.content.id))
|
|
1223
|
+
: undefined;
|
|
1138
1224
|
const content = event.content?.kind === 'text'
|
|
1139
1225
|
? {
|
|
1140
1226
|
...event.content,
|
|
@@ -1151,10 +1237,10 @@ function rewriteRuntimeEventReferences(event, references) {
|
|
|
1151
1237
|
: event.content?.kind === 'function_response'
|
|
1152
1238
|
? {
|
|
1153
1239
|
...event.content,
|
|
1154
|
-
result: rewriteRuntimeToolResult(event.content.result, references),
|
|
1240
|
+
result: rewriteRuntimeToolResult(event.content.result, references, toolName),
|
|
1155
1241
|
...(event.content.modelProjection
|
|
1156
1242
|
? {
|
|
1157
|
-
modelProjection:
|
|
1243
|
+
modelProjection: rewriteConversationCopyModelProjection(event.content.modelProjection, references, toolName),
|
|
1158
1244
|
}
|
|
1159
1245
|
: {}),
|
|
1160
1246
|
}
|
|
@@ -1249,7 +1335,24 @@ function rewriteToolRecoveryFact(recovery, operationId, references) {
|
|
|
1249
1335
|
},
|
|
1250
1336
|
};
|
|
1251
1337
|
}
|
|
1252
|
-
function rewriteToolResultContent(content, references) {
|
|
1338
|
+
function rewriteToolResultContent(content, references, toolName) {
|
|
1339
|
+
if (toolName === 'agent_output' && content.kind === 'json') {
|
|
1340
|
+
if (isConversationCopyAgentOutputSnapshot(content.value)) {
|
|
1341
|
+
return rewriteAgentOutputSnapshot(content.value, references);
|
|
1342
|
+
}
|
|
1343
|
+
const output = conversationCopyAgentOutput(content.value);
|
|
1344
|
+
if (output) {
|
|
1345
|
+
const shared = sharedChild(output.reference.childSessionId, references);
|
|
1346
|
+
if (!shared) {
|
|
1347
|
+
return rewriteAgentOutputSnapshot(output.snapshot, references);
|
|
1348
|
+
}
|
|
1349
|
+
// Revision children remain owned by their original physical Session.
|
|
1350
|
+
// Validate those external references without rewriting their identities.
|
|
1351
|
+
rewriteLinkedRunId(output.reference.runId, shared, references, 'AgentRun');
|
|
1352
|
+
rewriteLinkedArtifactIds(output.reference.artifactIds, shared, references);
|
|
1353
|
+
return content;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1253
1356
|
if (content.kind === 'image') {
|
|
1254
1357
|
return { ...content, ref: rewriteStorageRef(content.ref, references) };
|
|
1255
1358
|
}
|
|
@@ -1320,17 +1423,44 @@ function rewriteToolResultContent(content, references) {
|
|
|
1320
1423
|
}
|
|
1321
1424
|
return content;
|
|
1322
1425
|
}
|
|
1323
|
-
function
|
|
1426
|
+
function rewriteAgentOutputSnapshot(snapshot, references) {
|
|
1427
|
+
return {
|
|
1428
|
+
kind: 'json',
|
|
1429
|
+
value: {
|
|
1430
|
+
...snapshot,
|
|
1431
|
+
...(snapshot.text !== undefined
|
|
1432
|
+
? { text: rewriteAttachmentResourceRefs(snapshot.text, references.artifactIds) }
|
|
1433
|
+
: {}),
|
|
1434
|
+
artifactIds: rewriteArtifactIds(snapshot.artifactIds, references),
|
|
1435
|
+
},
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
function rewriteConversationCopyModelProjection(projection, references, toolName) {
|
|
1439
|
+
if (toolName === 'agent_output' &&
|
|
1440
|
+
projection.kind === 'json' &&
|
|
1441
|
+
(conversationCopyAgentOutput(projection.value) ||
|
|
1442
|
+
isConversationCopyAgentOutputSnapshot(projection.value))) {
|
|
1443
|
+
const content = rewriteToolResultContent({ kind: 'json', value: projection.value }, references, toolName);
|
|
1444
|
+
if (content.kind === 'json')
|
|
1445
|
+
return { ...projection, value: content.value };
|
|
1446
|
+
}
|
|
1447
|
+
return rewriteDurableToolResultProjectionArtifactRefs(projection, (ref) => rewriteProjectionArtifactRef(ref, references));
|
|
1448
|
+
}
|
|
1449
|
+
function rewriteRuntimeToolResult(value, references, toolName) {
|
|
1324
1450
|
if (isArchivedToolResultPlaceholder(value))
|
|
1325
1451
|
return rewriteArchivedToolResult(value, references);
|
|
1326
1452
|
let content;
|
|
1327
1453
|
try {
|
|
1328
|
-
content =
|
|
1454
|
+
content = decodeConversationCopyToolResult(value, toolName);
|
|
1329
1455
|
}
|
|
1330
1456
|
catch {
|
|
1331
1457
|
return value;
|
|
1332
1458
|
}
|
|
1333
|
-
|
|
1459
|
+
const rewritten = rewriteToolResultContent(content, references, toolName);
|
|
1460
|
+
return (conversationCopyAgentOutput(value) || isConversationCopyAgentOutputSnapshot(value)) &&
|
|
1461
|
+
rewritten.kind === 'json'
|
|
1462
|
+
? rewritten.value
|
|
1463
|
+
: rewritten;
|
|
1334
1464
|
}
|
|
1335
1465
|
function rewriteArtifactIds(artifactIds, references) {
|
|
1336
1466
|
return artifactIds.flatMap((artifactId) => {
|
|
@@ -1401,11 +1531,11 @@ function rewriteProjectionArtifactRef(ref, references) {
|
|
|
1401
1531
|
return rewritten;
|
|
1402
1532
|
}
|
|
1403
1533
|
function rewriteArchivedToolResult(value, references) {
|
|
1534
|
+
const migrated = references.ledgerArchives?.get(buildToolResultArchiveResourceRef(value));
|
|
1535
|
+
if (migrated)
|
|
1536
|
+
return migrated;
|
|
1404
1537
|
if (value.rewriteVersion === 2) {
|
|
1405
|
-
|
|
1406
|
-
if (!rewritten)
|
|
1407
|
-
throw new Error('Cannot copy ledger archive without its committed transition');
|
|
1408
|
-
return rewritten;
|
|
1538
|
+
throw new Error('Cannot copy ledger archive without its committed transition');
|
|
1409
1539
|
}
|
|
1410
1540
|
const artifactId = rewriteOwnedArtifactId(value.artifactId, references);
|
|
1411
1541
|
const resource = value.resourceRef
|