maka-agent 0.2.0-dev.30.20260912 → 0.2.0-dev.31.20260913
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/THIRD_PARTY_NOTICES.txt +392 -0
- package/dist/acp/maka-acp-agent.js +8 -2
- package/dist/acp/prompt-content.js +225 -0
- package/dist/acp/session-event-mapper.js +103 -0
- package/dist/acp/session-registry.js +654 -17
- package/dist/acp/stdio-server.js +10 -1
- package/dist/cli-core.js +1 -1
- package/dist/pi-transcript-tools.js +3 -2
- package/dist/pi-transcript.js +12 -2
- package/dist/runtime-host-session-channel.js +63 -9
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/artifacts.js +6 -0
- package/node_modules/@maka/core/dist/attachments.js +7 -2
- package/node_modules/@maka/core/dist/durable-tool-result-projection.js +3 -1
- package/node_modules/@maka/core/dist/events.js +21 -0
- package/node_modules/@maka/core/dist/goal.js +44 -2
- package/node_modules/@maka/core/dist/permission.js +0 -1
- package/node_modules/@maka/core/dist/runtime-boundary.js +73 -29
- package/node_modules/@maka/core/dist/runtime-event.js +6 -3
- package/node_modules/@maka/core/dist/runtime-handoff.js +10 -2
- package/node_modules/@maka/core/dist/runtime-logical-execution.js +39 -14
- package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +3 -1
- package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
- package/node_modules/@maka/core/dist/unified-diff.js +33 -26
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +49 -178
- package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +7 -2
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +15 -53
- package/node_modules/@maka/runtime/dist/bash-model-output.js +7 -1
- package/node_modules/@maka/runtime/dist/bash-tail-buffer.js +13 -52
- package/node_modules/@maka/runtime/dist/builtin-tools.js +59 -111
- package/node_modules/@maka/runtime/dist/child-fd-input.js +3 -2
- package/node_modules/@maka/runtime/dist/context-budget-policy.js +1 -11
- package/node_modules/@maka/runtime/dist/context-budget.js +12 -1
- package/node_modules/@maka/runtime/dist/conversation-copy.js +104 -9
- package/node_modules/@maka/runtime/dist/filesystem-executor.js +10 -6
- package/node_modules/@maka/runtime/dist/filesystem-worker/client.js +77 -19
- package/node_modules/@maka/runtime/dist/filesystem-worker/launch-spec.js +66 -55
- package/node_modules/@maka/runtime/dist/filesystem-worker/operations.js +34 -97
- package/node_modules/@maka/runtime/dist/filesystem-worker/protocol.js +25 -10
- package/node_modules/@maka/runtime/dist/filesystem-worker/worker-entry.js +2 -0
- package/node_modules/@maka/runtime/dist/glob-search.js +74 -0
- package/node_modules/@maka/runtime/dist/goal-continuation.js +78 -8
- package/node_modules/@maka/runtime/dist/goal-state.js +7 -0
- package/node_modules/@maka/runtime/dist/grep-search.js +155 -0
- package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +84 -94
- package/node_modules/@maka/runtime/dist/model-history.js +25 -3
- package/node_modules/@maka/runtime/dist/pipe-tail-collector.js +6 -9
- package/node_modules/@maka/runtime/dist/read-page.js +231 -0
- package/node_modules/@maka/runtime/dist/request-projection.js +2 -15
- package/node_modules/@maka/runtime/dist/ripgrep-executable.js +62 -0
- package/node_modules/@maka/runtime/dist/ripgrep-guidance.js +81 -0
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +187 -20
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +2 -1
- package/node_modules/@maka/runtime/dist/sandbox/linux-profile-path.js +7 -1
- package/node_modules/@maka/runtime/dist/sandbox/linux-sandbox.js +20 -5
- package/node_modules/@maka/runtime/dist/session-manager.js +6 -6
- package/node_modules/@maka/runtime/dist/session-recap.js +9 -5
- package/node_modules/@maka/runtime/dist/shell-exec.js +7 -25
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +1 -1
- package/node_modules/@maka/runtime/dist/shell-run-tool-result.js +1 -1
- package/node_modules/@maka/runtime/dist/shell-tools.js +6 -12
- package/node_modules/@maka/runtime/dist/tool-availability.js +0 -1
- package/node_modules/@maka/runtime/dist/tool-output.js +1 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-capability.js +24 -36
- package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +32 -527
- package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +17 -119
- package/node_modules/@maka/runtime/dist/tool-result-archive.js +7 -25
- package/node_modules/@maka/runtime/dist/tool-runtime.js +7 -5
- package/node_modules/@maka/runtime/dist/workers/filesystem-worker.js +6933 -3681
- package/node_modules/@maka/runtime/dist/workspace-executor.js +20 -24
- package/node_modules/@maka/runtime/package.json +2 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +185 -191
- package/node_modules/@maka/runtime-host/dist/client/reconnecting-connection.js +3 -0
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
- package/node_modules/@maka/runtime-host/dist/protocol/message.js +2 -2
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +2 -2
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +10 -2
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +38 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +2 -8
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +81 -7
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +13 -13
- package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +65 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +1 -0
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +88 -72
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +21 -4
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +145 -1
- package/node_modules/@maka/storage/dist/agent-run-store.js +5 -2
- package/node_modules/@maka/storage/dist/execution-stores.js +2 -1
- package/node_modules/@maka/storage/dist/runtime-transcript-query.js +27 -14
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +69 -3
- package/node_modules/balanced-match/LICENSE.md +23 -0
- package/node_modules/balanced-match/README.md +57 -0
- package/node_modules/balanced-match/dist/commonjs/index.js +59 -0
- package/node_modules/balanced-match/dist/commonjs/package.json +3 -0
- package/node_modules/balanced-match/dist/esm/index.js +54 -0
- package/node_modules/balanced-match/dist/esm/package.json +3 -0
- package/node_modules/balanced-match/package.json +68 -0
- package/node_modules/brace-expansion/LICENSE +23 -0
- package/node_modules/brace-expansion/README.md +105 -0
- package/node_modules/brace-expansion/dist/commonjs/index.js +289 -0
- package/node_modules/brace-expansion/dist/commonjs/package.json +3 -0
- package/node_modules/brace-expansion/dist/esm/index.js +285 -0
- package/node_modules/brace-expansion/dist/esm/package.json +3 -0
- package/node_modules/brace-expansion/package.json +64 -0
- package/node_modules/glob/LICENSE.md +63 -0
- package/node_modules/glob/README.md +1203 -0
- package/node_modules/glob/dist/commonjs/glob.js +248 -0
- package/node_modules/glob/dist/commonjs/has-magic.js +27 -0
- package/node_modules/glob/dist/commonjs/ignore.js +119 -0
- package/node_modules/glob/dist/commonjs/index.js +68 -0
- package/node_modules/glob/dist/commonjs/index.min.js +4 -0
- package/node_modules/glob/dist/commonjs/package.json +3 -0
- package/node_modules/glob/dist/commonjs/pattern.js +223 -0
- package/node_modules/glob/dist/commonjs/processor.js +301 -0
- package/node_modules/glob/dist/commonjs/walker.js +387 -0
- package/node_modules/glob/dist/esm/glob.js +244 -0
- package/node_modules/glob/dist/esm/has-magic.js +23 -0
- package/node_modules/glob/dist/esm/ignore.js +115 -0
- package/node_modules/glob/dist/esm/index.js +55 -0
- package/node_modules/glob/dist/esm/index.min.js +4 -0
- package/node_modules/glob/dist/esm/package.json +3 -0
- package/node_modules/glob/dist/esm/pattern.js +219 -0
- package/node_modules/glob/dist/esm/processor.js +294 -0
- package/node_modules/glob/dist/esm/walker.js +381 -0
- package/node_modules/glob/package.json +98 -0
- package/node_modules/minimatch/LICENSE.md +55 -0
- package/node_modules/minimatch/README.md +528 -0
- package/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js +14 -0
- package/node_modules/minimatch/dist/commonjs/ast.js +845 -0
- package/node_modules/minimatch/dist/commonjs/brace-expressions.js +150 -0
- package/node_modules/minimatch/dist/commonjs/escape.js +30 -0
- package/node_modules/minimatch/dist/commonjs/index.js +1127 -0
- package/node_modules/minimatch/dist/commonjs/package.json +3 -0
- package/node_modules/minimatch/dist/commonjs/unescape.js +38 -0
- package/node_modules/minimatch/dist/esm/assert-valid-pattern.js +10 -0
- package/node_modules/minimatch/dist/esm/ast.js +841 -0
- package/node_modules/minimatch/dist/esm/brace-expressions.js +146 -0
- package/node_modules/minimatch/dist/esm/escape.js +26 -0
- package/node_modules/minimatch/dist/esm/index.js +1114 -0
- package/node_modules/minimatch/dist/esm/package.json +3 -0
- package/node_modules/minimatch/dist/esm/unescape.js +34 -0
- package/node_modules/minimatch/package.json +73 -0
- package/node_modules/minipass/LICENSE.md +55 -0
- package/node_modules/minipass/README.md +825 -0
- package/node_modules/minipass/dist/commonjs/index.js +1038 -0
- package/node_modules/minipass/dist/commonjs/package.json +3 -0
- package/node_modules/minipass/dist/esm/index.js +1020 -0
- package/node_modules/minipass/dist/esm/package.json +3 -0
- package/node_modules/minipass/package.json +77 -0
- package/node_modules/path-scurry/LICENSE.md +55 -0
- package/node_modules/path-scurry/README.md +636 -0
- package/node_modules/path-scurry/dist/commonjs/index.js +2018 -0
- package/node_modules/path-scurry/dist/commonjs/package.json +3 -0
- package/node_modules/path-scurry/dist/esm/index.js +1983 -0
- package/node_modules/path-scurry/dist/esm/package.json +3 -0
- package/node_modules/path-scurry/node_modules/lru-cache/LICENSE.md +55 -0
- package/node_modules/path-scurry/node_modules/lru-cache/README.md +469 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +1726 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/package.json +3 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +1722 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/package.json +3 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/package.json +154 -0
- package/node_modules/path-scurry/package.json +72 -0
- package/package.json +1 -1
- package/node_modules/@maka/runtime/dist/active-tool-result-prune.js +0 -248
- package/node_modules/@maka/runtime/dist/active-tool-result-working-set.js +0 -218
- package/node_modules/@maka/runtime/dist/archive-read-tool.js +0 -115
package/dist/acp/stdio-server.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { Readable, Writable } from 'node:stream';
|
|
20
20
|
import { ndJsonStream } from '@agentclientprotocol/sdk';
|
|
21
|
+
import { isRuntimeHostReconnectingConnection, } from '@maka/runtime-host/client';
|
|
21
22
|
import { createMakaAcpAgent } from './maka-acp-agent.js';
|
|
22
23
|
import { AcpSessionRegistry } from './session-registry.js';
|
|
23
24
|
import { connectRuntimeHostCliConnection } from '../runtime-host-cli-context.js';
|
|
@@ -29,8 +30,16 @@ export async function runMakaAcpStdioServer(input, dependencies = {}) {
|
|
|
29
30
|
clientDataRoot: input.clientDataRoot,
|
|
30
31
|
signal,
|
|
31
32
|
});
|
|
33
|
+
const connection = context.connection;
|
|
34
|
+
if (!isRuntimeHostReconnectingConnection(connection)) {
|
|
35
|
+
await context.close().catch(() => undefined);
|
|
36
|
+
throw new Error('ACP requires a reconnecting Runtime Host connection');
|
|
37
|
+
}
|
|
32
38
|
return {
|
|
33
|
-
|
|
39
|
+
reconnecting: true,
|
|
40
|
+
request: connection.request.bind(connection),
|
|
41
|
+
openSessionSubscription: connection.openSessionSubscription.bind(connection),
|
|
42
|
+
openSessionSubscriptionOnce: connection.openSessionSubscriptionOnce.bind(connection),
|
|
34
43
|
close: () => context.close(),
|
|
35
44
|
};
|
|
36
45
|
},
|
package/dist/cli-core.js
CHANGED
|
@@ -101,7 +101,7 @@ function helpText(cliCommand) {
|
|
|
101
101
|
'Commands:',
|
|
102
102
|
...[
|
|
103
103
|
['', 'Start the TUI'],
|
|
104
|
-
['--acp', 'Serve ACP v1 over stdio (
|
|
104
|
+
['--acp', 'Serve ACP v1 over stdio (sessions, prompts, streaming, cancellation)'],
|
|
105
105
|
['run ...', 'Run one non-interactive model turn'],
|
|
106
106
|
['-p ...', `Alias for ${cliCommand} run`],
|
|
107
107
|
['activate ...', 'Run one Cloud Session activation and emit JSONL'],
|
|
@@ -378,11 +378,12 @@ function readInputRef(entry) {
|
|
|
378
378
|
}
|
|
379
379
|
/** A Read using the filesystem branch. */
|
|
380
380
|
function isFilesystemReadPath(entry) {
|
|
381
|
-
|
|
381
|
+
const path = readInputPath(entry);
|
|
382
|
+
return path !== undefined && !path.startsWith('maka:');
|
|
382
383
|
}
|
|
383
384
|
/** A Read using the runtime-resource branch (background-task output, etc.). */
|
|
384
385
|
function isRuntimeResourceRead(entry) {
|
|
385
|
-
return readInputRef(entry)?.startsWith('maka
|
|
386
|
+
return (readInputPath(entry) ?? readInputRef(entry))?.startsWith('maka:') ?? false;
|
|
386
387
|
}
|
|
387
388
|
/**
|
|
388
389
|
* True only for the result shapes a filesystem Read uses to carry actual file
|
package/dist/pi-transcript.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { Markdown, visibleWidth } from '@earendil-works/pi-tui';
|
|
20
|
+
import { resolveReadInput } from '@maka/runtime/read-page';
|
|
20
21
|
import { deriveTurnRecords, isRuntimeSystemNoteKind, STEP_LIMIT_NOTICE_TEXT, } from '@maka/core/session';
|
|
21
22
|
import { providerRetryDisplaySeconds } from '@maka/core/provider-retry-countdown';
|
|
22
23
|
import { defineUiMessageCatalog, resolveUiMessageCatalog, } from '@maka/core/ui-locale';
|
|
@@ -1578,8 +1579,17 @@ function findShellRunParent(state, ref, childToolUseId) {
|
|
|
1578
1579
|
}
|
|
1579
1580
|
/** The runtime-resource ref a tool call is aimed at, when the args carry one. */
|
|
1580
1581
|
function readArgsRef(args) {
|
|
1581
|
-
const ref = args !== null && typeof args === 'object'
|
|
1582
|
-
|
|
1582
|
+
const ref = args !== null && typeof args === 'object'
|
|
1583
|
+
? (args.path ?? args.ref)
|
|
1584
|
+
: undefined;
|
|
1585
|
+
if (typeof ref !== 'string' || !ref)
|
|
1586
|
+
return undefined;
|
|
1587
|
+
try {
|
|
1588
|
+
return resolveReadInput({ path: ref }).path;
|
|
1589
|
+
}
|
|
1590
|
+
catch {
|
|
1591
|
+
return undefined;
|
|
1592
|
+
}
|
|
1583
1593
|
}
|
|
1584
1594
|
/**
|
|
1585
1595
|
* A card whose run resource is still `running`. The transition is keyed on the
|
|
@@ -59,6 +59,8 @@ export class RuntimeHostSessionChannel {
|
|
|
59
59
|
#activated = false;
|
|
60
60
|
#startedTurnBarrier;
|
|
61
61
|
#closing = false;
|
|
62
|
+
#closeController = new AbortController();
|
|
63
|
+
#closeTask;
|
|
62
64
|
#failure;
|
|
63
65
|
#recoveryTask;
|
|
64
66
|
#recoveryAttemptsWithoutLiveFrame = 0;
|
|
@@ -93,18 +95,27 @@ export class RuntimeHostSessionChannel {
|
|
|
93
95
|
});
|
|
94
96
|
}
|
|
95
97
|
static async open(options) {
|
|
96
|
-
const
|
|
98
|
+
const openInitial = options.openInitialSessionSubscription ??
|
|
99
|
+
options.connection.openSessionSubscription.bind(options.connection);
|
|
100
|
+
const subscription = await runChannelOperation(() => openInitial({
|
|
97
101
|
sessionId: options.sessionId,
|
|
98
102
|
transcript: {
|
|
99
103
|
kind: 'tail',
|
|
100
104
|
maxBytes: SESSION_TRANSCRIPT_BOOTSTRAP_MAX_BYTES,
|
|
101
105
|
},
|
|
102
|
-
});
|
|
106
|
+
}), options.signal, (lateSubscription) => lateSubscription.close());
|
|
103
107
|
const initialRoot = structuredClone(subscription.snapshot.rootTurn);
|
|
104
108
|
const channel = new RuntimeHostSessionChannel(subscription, [], options, options.connection);
|
|
109
|
+
const onAbort = () => {
|
|
110
|
+
void channel.close().catch(() => undefined);
|
|
111
|
+
};
|
|
112
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
113
|
+
if (options.signal?.aborted)
|
|
114
|
+
onAbort();
|
|
105
115
|
void channel.#pump(subscription);
|
|
106
116
|
try {
|
|
107
117
|
const recovered = await channel.#hydrateInitial(subscription);
|
|
118
|
+
options.signal?.throwIfAborted();
|
|
108
119
|
const root = recovered ? structuredClone(channel.snapshot.rootTurn) : initialRoot;
|
|
109
120
|
return {
|
|
110
121
|
channel,
|
|
@@ -117,11 +128,15 @@ export class RuntimeHostSessionChannel {
|
|
|
117
128
|
await channel.close().catch(() => undefined);
|
|
118
129
|
throw error;
|
|
119
130
|
}
|
|
131
|
+
finally {
|
|
132
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
133
|
+
}
|
|
120
134
|
}
|
|
121
135
|
async #hydrateInitial(subscription) {
|
|
122
136
|
let messages;
|
|
123
137
|
try {
|
|
124
|
-
messages = await subscription.loadTranscript(decodeStoredMessage);
|
|
138
|
+
messages = await runChannelOperation(() => subscription.loadTranscript(decodeStoredMessage), this.#closeController.signal);
|
|
139
|
+
this.#closeController.signal.throwIfAborted();
|
|
125
140
|
}
|
|
126
141
|
catch (error) {
|
|
127
142
|
if (!this.#canRecover(error))
|
|
@@ -242,10 +257,13 @@ export class RuntimeHostSessionChannel {
|
|
|
242
257
|
});
|
|
243
258
|
}
|
|
244
259
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
260
|
+
close() {
|
|
261
|
+
this.#closeTask ??= this.#close();
|
|
262
|
+
return this.#closeTask;
|
|
263
|
+
}
|
|
264
|
+
async #close() {
|
|
248
265
|
this.#closing = true;
|
|
266
|
+
this.#closeController.abort(new Error('Runtime Host Session channel is closed'));
|
|
249
267
|
this.#clearRecoveryStableTimer();
|
|
250
268
|
this.#recoveryAwaitingLiveFrame = undefined;
|
|
251
269
|
this.#pendingStartedTurns.clear();
|
|
@@ -337,13 +355,13 @@ export class RuntimeHostSessionChannel {
|
|
|
337
355
|
return;
|
|
338
356
|
let replacement;
|
|
339
357
|
try {
|
|
340
|
-
replacement = await this.#connection.openSessionSubscription({
|
|
358
|
+
replacement = await runChannelOperation(() => this.#connection.openSessionSubscription({
|
|
341
359
|
sessionId: this.sessionId,
|
|
342
360
|
transcript: {
|
|
343
361
|
kind: 'tail',
|
|
344
362
|
maxBytes: SESSION_TRANSCRIPT_BOOTSTRAP_MAX_BYTES,
|
|
345
363
|
},
|
|
346
|
-
});
|
|
364
|
+
}), this.#closeController.signal, (lateSubscription) => lateSubscription.close());
|
|
347
365
|
}
|
|
348
366
|
catch (error) {
|
|
349
367
|
if (this.#canRecover(error))
|
|
@@ -360,7 +378,7 @@ export class RuntimeHostSessionChannel {
|
|
|
360
378
|
this.#pendingFrames.length = 0;
|
|
361
379
|
void this.#pump(replacement);
|
|
362
380
|
try {
|
|
363
|
-
const messages = await replacement.loadTranscript(decodeStoredMessage);
|
|
381
|
+
const messages = await runChannelOperation(() => replacement.loadTranscript(decodeStoredMessage), this.#closeController.signal);
|
|
364
382
|
if (this.#failedSubscriptions.has(replacement)) {
|
|
365
383
|
throw new RuntimeHostSubscriptionError('connection_closed', 'Runtime Host Session subscription closed during catch-up');
|
|
366
384
|
}
|
|
@@ -759,6 +777,42 @@ function isGuaranteedOutcome(event) {
|
|
|
759
777
|
function isTurnTerminalOutcome(event) {
|
|
760
778
|
return event.type === 'complete' || event.type === 'abort' || event.type === 'error';
|
|
761
779
|
}
|
|
780
|
+
function runChannelOperation(operation, signal, discard) {
|
|
781
|
+
if (!signal)
|
|
782
|
+
return operation();
|
|
783
|
+
if (signal.aborted)
|
|
784
|
+
return Promise.reject(signal.reason);
|
|
785
|
+
return new Promise((resolve, reject) => {
|
|
786
|
+
let aborted = false;
|
|
787
|
+
const onAbort = () => {
|
|
788
|
+
aborted = true;
|
|
789
|
+
signal.removeEventListener('abort', onAbort);
|
|
790
|
+
reject(signal.reason);
|
|
791
|
+
};
|
|
792
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
793
|
+
let running;
|
|
794
|
+
try {
|
|
795
|
+
running = operation();
|
|
796
|
+
}
|
|
797
|
+
catch (error) {
|
|
798
|
+
signal.removeEventListener('abort', onAbort);
|
|
799
|
+
reject(error);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
void running.then((value) => {
|
|
803
|
+
signal.removeEventListener('abort', onAbort);
|
|
804
|
+
// The connection may finish opening after this channel has gone away.
|
|
805
|
+
// A late subscription still belongs to the operation and must be closed.
|
|
806
|
+
if (aborted)
|
|
807
|
+
void discard?.(value).catch(() => undefined);
|
|
808
|
+
else
|
|
809
|
+
resolve(value);
|
|
810
|
+
}, (error) => {
|
|
811
|
+
signal.removeEventListener('abort', onAbort);
|
|
812
|
+
reject(error);
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
}
|
|
762
816
|
/**
|
|
763
817
|
* Goal identity + revision: GoalManager.commit bumps the revision on every
|
|
764
818
|
* accepted transition, so this pair detects every set/settle/pause/resume/
|
|
Binary file
|
|
@@ -96,6 +96,12 @@ const CHILD_RESULT_OUTPUT_SOURCES = new Set([
|
|
|
96
96
|
'deep_research',
|
|
97
97
|
]);
|
|
98
98
|
export function isArtifactUserVisible(record) {
|
|
99
|
+
// A directly written HTML file is an intentional user-facing deliverable:
|
|
100
|
+
// the Artifact Pane must be able to preview and open it without requiring a
|
|
101
|
+
// child-workspace writeback. Other tool results remain internal to avoid
|
|
102
|
+
// flooding the Generated Files tab with command output and diffs.
|
|
103
|
+
if (record.source === 'tool_result' && record.kind === 'html')
|
|
104
|
+
return true;
|
|
99
105
|
return ARTIFACT_SOURCE_POLICIES[record.source].userVisible;
|
|
100
106
|
}
|
|
101
107
|
/** Visibility does not grant permission to destroy evidence owned by a runtime workflow. */
|
|
@@ -251,6 +251,11 @@ export function attachmentKindFromMimeType(mimeType, fileName) {
|
|
|
251
251
|
}
|
|
252
252
|
return 'other';
|
|
253
253
|
}
|
|
254
|
-
export
|
|
255
|
-
|
|
254
|
+
export class AttachmentIngestBlockedError extends Error {
|
|
255
|
+
code;
|
|
256
|
+
constructor(code) {
|
|
257
|
+
super('Attachment ingest was blocked');
|
|
258
|
+
this.code = code;
|
|
259
|
+
this.name = 'AttachmentIngestBlockedError';
|
|
260
|
+
}
|
|
256
261
|
}
|
|
@@ -21,7 +21,9 @@ import { isCanonicalArtifactEntityId, normalizeArtifactImagePreviewMime } from '
|
|
|
21
21
|
import { hasExactShape, isRecord } from './record-schema.js';
|
|
22
22
|
import { serializedByteLength } from './serialized-byte-length.js';
|
|
23
23
|
export const DURABLE_TOOL_RESULT_PROJECTION_VERSION = 1;
|
|
24
|
-
|
|
24
|
+
// Storage capacity, not a provider context budget. Two executor-retained Bash
|
|
25
|
+
// streams can require 12 MiB after JSON escaping; Read bounds their presentation.
|
|
26
|
+
export const DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES = 16 * 1024 * 1024;
|
|
25
27
|
export const DURABLE_TOOL_RESULT_PROJECTION_MAX_PARTS = 64;
|
|
26
28
|
export const DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_DEPTH = 32;
|
|
27
29
|
export const DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_NODES = 20_000;
|
|
@@ -57,6 +57,27 @@ export function isDirectoryReference(value) {
|
|
|
57
57
|
isCanonicalAbsolutePath(value.path));
|
|
58
58
|
}
|
|
59
59
|
const MESSAGE_CONTENT_SHAPE = defineObjectShape()(['text'], ['displayText', 'attachments', 'directoryReferences', 'quotes', 'inlineReferences']);
|
|
60
|
+
/**
|
|
61
|
+
* A Turn message is meaningful when at least one of its four content carriers
|
|
62
|
+
* is present: inline text, an inline excerpt, an attachment reference, or a
|
|
63
|
+
* directory reference. Admission, compaction estimates, replay visibility,
|
|
64
|
+
* and recap projection must share this one predicate (#4804) — restating it
|
|
65
|
+
* per layer is how a quote-only message ends up admitted by one boundary and
|
|
66
|
+
* silently dropped by the next.
|
|
67
|
+
*
|
|
68
|
+
* The inline text is deliberately NOT trimmed. Admission asks "is this frame
|
|
69
|
+
* legal"; replay visibility asks "will the model see this already-persisted
|
|
70
|
+
* event", and that answer must stay compatible with everything admission has
|
|
71
|
+
* ever accepted — trimming here retroactively re-reads stored history as
|
|
72
|
+
* invisible and blocks replay on it (#4815 review). Surfaces that want the
|
|
73
|
+
* trimmed judgement (the desktop guard) trim at their own boundary.
|
|
74
|
+
*/
|
|
75
|
+
export function hasMeaningfulMessageContent(content) {
|
|
76
|
+
return (content.text.length > 0 ||
|
|
77
|
+
(content.quotes?.length ?? 0) > 0 ||
|
|
78
|
+
(content.attachments?.length ?? 0) > 0 ||
|
|
79
|
+
(content.directoryReferences?.length ?? 0) > 0);
|
|
80
|
+
}
|
|
60
81
|
const ATTACHMENT_REF_SHAPE = defineObjectShape()(['kind', 'name', 'mimeType', 'bytes', 'ref'], []);
|
|
61
82
|
const QUOTE_REF_SHAPE = defineObjectShape()(['text'], ['label', 'sourceTurnId']);
|
|
62
83
|
const INLINE_REFERENCE_SHAPE = defineObjectShape()(['kind', 'value', 'label', 'start'], []);
|
|
@@ -68,10 +68,14 @@ const GOAL_CONTROL_LEASE_SHAPE = defineObjectShape()(['goalId', 'generation'], [
|
|
|
68
68
|
const GOAL_EXECUTION_REF_SHAPE = defineObjectShape()(['sessionId', 'turnId', 'runId'], []);
|
|
69
69
|
const GOAL_CHECKPOINT_SHAPE = defineObjectShape()(['goalId', 'revision'], []);
|
|
70
70
|
const GOAL_CURRENT_EXECUTION_SHAPE = defineObjectShape()(['execution', 'checkpoint', 'controlLease'], []);
|
|
71
|
-
const
|
|
71
|
+
const GOAL_PENDING_CONTINUATION_SHAPE = defineObjectShape()(['checkpoint', 'controlLease', 'prompt'], ['triggeringTurnId']);
|
|
72
|
+
const GOAL_AUTHORITY_RECORD_SHAPE = defineObjectShape()(['schemaVersion', 'goal', 'controlLease', 'currentExecution', 'pendingContinuation'], []);
|
|
73
|
+
const GOAL_AUTHORITY_RECORD_V1_SHAPE = defineObjectShape()(['schemaVersion', 'goal', 'controlLease', 'currentExecution'], []);
|
|
72
74
|
const GOAL_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
73
75
|
export function decodeGoalAuthorityRecord(value) {
|
|
74
|
-
if (!isRecord(value) ||
|
|
76
|
+
if (!isRecord(value) ||
|
|
77
|
+
(!hasExactShape(value, GOAL_AUTHORITY_RECORD_SHAPE) &&
|
|
78
|
+
!hasExactShape(value, GOAL_AUTHORITY_RECORD_V1_SHAPE))) {
|
|
75
79
|
throw new TypeError('Invalid Goal authority record');
|
|
76
80
|
}
|
|
77
81
|
if (value.schemaVersion !== 1)
|
|
@@ -81,6 +85,12 @@ export function decodeGoalAuthorityRecord(value) {
|
|
|
81
85
|
const currentExecution = value.currentExecution === null
|
|
82
86
|
? null
|
|
83
87
|
: decodeGoalCurrentExecution(value.currentExecution, goal, controlLease);
|
|
88
|
+
const pendingContinuation = value.pendingContinuation === undefined || value.pendingContinuation === null
|
|
89
|
+
? null
|
|
90
|
+
: decodeGoalPendingContinuation(value.pendingContinuation, goal, controlLease);
|
|
91
|
+
if (currentExecution && pendingContinuation) {
|
|
92
|
+
throw new TypeError('Goal authority cannot have current and pending execution together');
|
|
93
|
+
}
|
|
84
94
|
if (controlLease.goalId !== goal.id) {
|
|
85
95
|
throw new TypeError('Goal control lease does not match Goal identity');
|
|
86
96
|
}
|
|
@@ -89,6 +99,30 @@ export function decodeGoalAuthorityRecord(value) {
|
|
|
89
99
|
goal,
|
|
90
100
|
controlLease,
|
|
91
101
|
currentExecution,
|
|
102
|
+
pendingContinuation,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function decodeGoalPendingContinuation(value, goal, authorityLease) {
|
|
106
|
+
if (!isRecord(value) || !hasExactShape(value, GOAL_PENDING_CONTINUATION_SHAPE)) {
|
|
107
|
+
throw new TypeError('Invalid Goal pending continuation');
|
|
108
|
+
}
|
|
109
|
+
const checkpoint = decodeGoalCheckpoint(value.checkpoint);
|
|
110
|
+
const controlLease = decodeGoalControlLease(value.controlLease);
|
|
111
|
+
if (checkpoint.goalId !== goal.id ||
|
|
112
|
+
checkpoint.revision !== goal.revision ||
|
|
113
|
+
controlLease.goalId !== authorityLease.goalId ||
|
|
114
|
+
controlLease.generation !== authorityLease.generation ||
|
|
115
|
+
typeof value.prompt !== 'string' ||
|
|
116
|
+
!value.prompt.trim() ||
|
|
117
|
+
!isGoalTextWithinLimit(value.prompt, GOAL_PENDING_PROMPT_TEXT_LIMIT) ||
|
|
118
|
+
!isOptionalGoalId(value.triggeringTurnId)) {
|
|
119
|
+
throw new TypeError('Goal pending continuation does not match Goal authority');
|
|
120
|
+
}
|
|
121
|
+
return Object.freeze({
|
|
122
|
+
checkpoint,
|
|
123
|
+
controlLease,
|
|
124
|
+
prompt: value.prompt,
|
|
125
|
+
...(value.triggeringTurnId !== undefined ? { triggeringTurnId: value.triggeringTurnId } : {}),
|
|
92
126
|
});
|
|
93
127
|
}
|
|
94
128
|
function decodeGoalState(value) {
|
|
@@ -166,6 +200,9 @@ function decodeGoalExecutionRef(value) {
|
|
|
166
200
|
function isGoalId(value) {
|
|
167
201
|
return typeof value === 'string' && GOAL_ID_PATTERN.test(value);
|
|
168
202
|
}
|
|
203
|
+
function isOptionalGoalId(value) {
|
|
204
|
+
return value === undefined || isGoalId(value);
|
|
205
|
+
}
|
|
169
206
|
function isNonnegativeInteger(value) {
|
|
170
207
|
return Number.isSafeInteger(value) && value >= 0;
|
|
171
208
|
}
|
|
@@ -206,3 +243,8 @@ export const GOAL_REASON_TEXT_LIMIT = Object.freeze({
|
|
|
206
243
|
codeUnits: 500,
|
|
207
244
|
utf8Bytes: 1_500,
|
|
208
245
|
});
|
|
246
|
+
/** Durable continuation prompts include bounded evaluator and Goal context. */
|
|
247
|
+
export const GOAL_PENDING_PROMPT_TEXT_LIMIT = Object.freeze({
|
|
248
|
+
codeUnits: 4_000,
|
|
249
|
+
utf8Bytes: 12_000,
|
|
250
|
+
});
|
|
@@ -22,30 +22,30 @@ import { isRecord } from './record-schema.js';
|
|
|
22
22
|
import { decodeRuntimeInvocationOpened, TOOL_BOUNDARY_PROTOCOL_V1 } from './runtime-event.js';
|
|
23
23
|
import { stableJsonStringify } from './tool-args-identity.js';
|
|
24
24
|
export function buildImmutableRuntimePrefix(identity, rows) {
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
if (!last)
|
|
28
|
-
throw new Error('immutable RuntimeEvent prefix is empty');
|
|
25
|
+
const events = [];
|
|
26
|
+
const proof = foldImmutableRuntimePrefix(identity, rows, (event) => events.push(event));
|
|
29
27
|
return {
|
|
30
28
|
protocol: 'immutable_runtime_prefix_v1',
|
|
31
|
-
identity:
|
|
32
|
-
position:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
lastEventId: last.event.id,
|
|
36
|
-
},
|
|
37
|
-
prefixDigest: digestCanonicalRuntimePrefix(identity, canonicalRows),
|
|
38
|
-
events: canonicalRows.map((row) => row.event),
|
|
29
|
+
identity: proof.identity,
|
|
30
|
+
position: proof.position,
|
|
31
|
+
prefixDigest: proof.prefixDigest,
|
|
32
|
+
events,
|
|
39
33
|
};
|
|
40
34
|
}
|
|
35
|
+
export function buildImmutableRuntimePrefixProof(identity, rows) {
|
|
36
|
+
return foldImmutableRuntimePrefix(identity, rows);
|
|
37
|
+
}
|
|
41
38
|
export function digestRuntimePrefix(identity, rows) {
|
|
42
|
-
return
|
|
39
|
+
return foldImmutableRuntimePrefix(identity, rows).prefixDigest;
|
|
43
40
|
}
|
|
44
41
|
export function runtimePrefixSegment(prefix) {
|
|
45
|
-
if (prefix.protocol !== 'immutable_runtime_prefix_v1'
|
|
42
|
+
if (prefix.protocol !== 'immutable_runtime_prefix_v1' &&
|
|
43
|
+
prefix.protocol !== 'immutable_runtime_prefix_proof_v1') {
|
|
46
44
|
throw new Error('Invalid immutable RuntimeEvent prefix protocol');
|
|
47
45
|
}
|
|
48
|
-
const rebuilt =
|
|
46
|
+
const rebuilt = prefix.protocol === 'immutable_runtime_prefix_v1'
|
|
47
|
+
? buildImmutableRuntimePrefix(prefix.identity, prefix.events.map((event, index) => ({ eventSeq: index + 1, event })))
|
|
48
|
+
: validateImmutableRuntimePrefixProof(prefix);
|
|
49
49
|
if (stableJsonStringify(rebuilt.position) !== stableJsonStringify(prefix.position)) {
|
|
50
50
|
throw new Error('Immutable RuntimeEvent prefix position mismatch');
|
|
51
51
|
}
|
|
@@ -290,11 +290,16 @@ startKind) {
|
|
|
290
290
|
prefixDigest: source.prefixDigest,
|
|
291
291
|
}));
|
|
292
292
|
}
|
|
293
|
-
function
|
|
293
|
+
function foldImmutableRuntimePrefix(identity, rows, visit) {
|
|
294
294
|
const canonicalIdentity = decodePrefixIdentity(identity);
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
295
|
+
const hash = nodeCrypto.createHash('sha256');
|
|
296
|
+
updateLengthPrefixed(hash, Buffer.from('maka.runtime-prefix.v1', 'utf8'));
|
|
297
|
+
updateLengthPrefixed(hash, Buffer.from(stableJsonStringify(canonicalIdentity), 'utf8'));
|
|
298
|
+
let eventCount = 0;
|
|
299
|
+
let firstEvent;
|
|
300
|
+
let lastEvent;
|
|
301
|
+
for (const row of rows) {
|
|
302
|
+
const expectedEventSeq = eventCount + 1;
|
|
298
303
|
if (!Number.isSafeInteger(row.eventSeq) ||
|
|
299
304
|
row.eventSeq <= 0 ||
|
|
300
305
|
row.eventSeq !== expectedEventSeq) {
|
|
@@ -310,19 +315,58 @@ function canonicalizePrefixRows(identity, rows) {
|
|
|
310
315
|
event.turnId !== canonicalIdentity.turnId) {
|
|
311
316
|
throw new Error(`immutable RuntimeEvent identity mismatch for ${event.id}`);
|
|
312
317
|
}
|
|
313
|
-
|
|
318
|
+
hash.update(uint64be(row.eventSeq));
|
|
319
|
+
updateLengthPrefixed(hash, Buffer.from(encodeRuntimePrefixV1Event(event), 'utf8'));
|
|
320
|
+
firstEvent ??= event;
|
|
321
|
+
lastEvent = event;
|
|
322
|
+
eventCount += 1;
|
|
323
|
+
visit?.(event);
|
|
314
324
|
}
|
|
315
|
-
|
|
325
|
+
if (!firstEvent || !lastEvent)
|
|
326
|
+
throw new Error('immutable RuntimeEvent prefix is empty');
|
|
327
|
+
return {
|
|
328
|
+
protocol: 'immutable_runtime_prefix_proof_v1',
|
|
329
|
+
identity: canonicalIdentity,
|
|
330
|
+
position: {
|
|
331
|
+
lastEventSeq: eventCount,
|
|
332
|
+
eventCount,
|
|
333
|
+
lastEventId: lastEvent.id,
|
|
334
|
+
},
|
|
335
|
+
prefixDigest: `sha256:${hash.digest('hex')}`,
|
|
336
|
+
firstEvent,
|
|
337
|
+
lastEvent,
|
|
338
|
+
};
|
|
316
339
|
}
|
|
317
|
-
function
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
340
|
+
function validateImmutableRuntimePrefixProof(proof) {
|
|
341
|
+
const identity = decodePrefixIdentity(proof.identity);
|
|
342
|
+
const position = decodePrefixPosition(proof.position);
|
|
343
|
+
const prefixDigest = decodeBoundaryDigest(proof.prefixDigest);
|
|
344
|
+
const firstEvent = encodeCanonicalRuntimeEvent(proof.firstEvent).event;
|
|
345
|
+
const lastEvent = encodeCanonicalRuntimeEvent(proof.lastEvent).event;
|
|
346
|
+
for (const event of [firstEvent, lastEvent]) {
|
|
347
|
+
if (event.partial === true)
|
|
348
|
+
throw new Error('Immutable RuntimeEvent proof contains a partial endpoint');
|
|
349
|
+
if (event.sessionId !== identity.sessionId ||
|
|
350
|
+
event.invocationId !== identity.invocationId ||
|
|
351
|
+
event.runId !== identity.runId ||
|
|
352
|
+
event.turnId !== identity.turnId) {
|
|
353
|
+
throw new Error(`Immutable RuntimeEvent proof identity mismatch for ${event.id}`);
|
|
354
|
+
}
|
|
324
355
|
}
|
|
325
|
-
|
|
356
|
+
if (position.lastEventId !== lastEvent.id) {
|
|
357
|
+
throw new Error('Immutable RuntimeEvent prefix position mismatch');
|
|
358
|
+
}
|
|
359
|
+
if (position.eventCount === 1 && firstEvent.id !== lastEvent.id) {
|
|
360
|
+
throw new Error('Immutable RuntimeEvent proof endpoints mismatch');
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
protocol: 'immutable_runtime_prefix_proof_v1',
|
|
364
|
+
identity,
|
|
365
|
+
position,
|
|
366
|
+
prefixDigest,
|
|
367
|
+
firstEvent,
|
|
368
|
+
lastEvent,
|
|
369
|
+
};
|
|
326
370
|
}
|
|
327
371
|
function encodeRuntimePrefixV1Event(event) {
|
|
328
372
|
const canonical = encodeCanonicalRuntimeEvent(event);
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
import { isWorkHubActionReceipt } from './workhub-action-result.js';
|
|
35
35
|
import { isModelRetryDecision } from './model-failure.js';
|
|
36
36
|
import { isRuntimeHandoffPause, runtimeHandoffPause, } from './runtime-handoff.js';
|
|
37
|
-
import { isMessageContent, normalizeMessageContent, } from './events.js';
|
|
37
|
+
import { hasMeaningfulMessageContent, isMessageContent, normalizeMessageContent, } from './events.js';
|
|
38
38
|
import { INTERACTION_ID_MAX_BYTES, INTERACTION_TOOL_NAME_MAX_BYTES, decodeInteractionRequest, } from './interaction.js';
|
|
39
39
|
import { isPermissionMode, } from './permission.js';
|
|
40
40
|
import { isCollaborationMode } from './collaboration.js';
|
|
@@ -818,7 +818,10 @@ export function isPartialRuntimeEvent(event) {
|
|
|
818
818
|
/**
|
|
819
819
|
* True if the event carries content whose kind is eligible for model
|
|
820
820
|
* history projection: text, thinking, function_call, or function_response.
|
|
821
|
-
*
|
|
821
|
+
* A user-authored text event with structured context (quotes or attachments)
|
|
822
|
+
* is model-visible even when the inline text is empty — the structured part
|
|
823
|
+
* is what carries the turn (#4804). Error-only content and pure action/refs
|
|
824
|
+
* events are NOT model-visible.
|
|
822
825
|
*
|
|
823
826
|
* This is a content-kind check only. Callers still apply `partial`
|
|
824
827
|
* filtering (partial chunks are never replayed into the next model call).
|
|
@@ -831,7 +834,7 @@ export function runtimeEventHasModelVisibleContent(event) {
|
|
|
831
834
|
return false;
|
|
832
835
|
switch (content.kind) {
|
|
833
836
|
case 'text':
|
|
834
|
-
return content
|
|
837
|
+
return hasMeaningfulMessageContent(content);
|
|
835
838
|
case 'thinking':
|
|
836
839
|
case 'function_call':
|
|
837
840
|
case 'function_response':
|
|
@@ -69,15 +69,23 @@ export function runtimeHandoffPause(event) {
|
|
|
69
69
|
}
|
|
70
70
|
/** Verify against source facts, not the proposed claim's description of them. */
|
|
71
71
|
export function assertHandoffClaimSource(claim, prefix) {
|
|
72
|
-
const
|
|
72
|
+
const first = prefix.protocol === 'immutable_runtime_prefix_v1' ? prefix.events[0] : prefix.firstEvent;
|
|
73
|
+
const last = prefix.protocol === 'immutable_runtime_prefix_v1' ? prefix.events.at(-1) : prefix.lastEvent;
|
|
73
74
|
const pause = last && runtimeHandoffPause(last);
|
|
74
75
|
const source = claim.targetOpening.source;
|
|
76
|
+
const claimedPrefix = claim.boundary.segments.at(-1);
|
|
77
|
+
if (!claimedPrefix ||
|
|
78
|
+
claimedPrefix.prefixDigest !== prefix.prefixDigest ||
|
|
79
|
+
stableJsonStringify(claimedPrefix.identity) !== stableJsonStringify(prefix.identity) ||
|
|
80
|
+
stableJsonStringify(claimedPrefix.position) !== stableJsonStringify(prefix.position)) {
|
|
81
|
+
throw new Error('Handoff claim does not preserve the sealed source prefix');
|
|
82
|
+
}
|
|
75
83
|
if (source.kind !== 'handoff') {
|
|
76
84
|
if (pause)
|
|
77
85
|
throw new Error('A handoff pause is reserved for its authorized successor');
|
|
78
86
|
return;
|
|
79
87
|
}
|
|
80
|
-
const opening =
|
|
88
|
+
const opening = first?.content;
|
|
81
89
|
if (!pause ||
|
|
82
90
|
opening?.kind !== 'invocation_opened' ||
|
|
83
91
|
prefix.identity.sessionId !== claim.target.sessionId ||
|