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
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
+
import { DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES } from '@maka/core/durable-tool-result-projection';
|
|
19
20
|
import { readRunInvocation } from '@maka/core/runtime-event-store';
|
|
20
21
|
import { runtimeHandoffPause } from '@maka/core/runtime-handoff';
|
|
21
22
|
import { readLogicalRuntimeExecution } from '@maka/core/runtime-logical-execution';
|
|
22
23
|
import { WORKHUB_COORDINATION_SESSION_ID } from '@maka/core/session';
|
|
23
|
-
import {
|
|
24
|
+
import { createRuntimeEventStoredMessageProjector, projectTranscriptToolResult, isHardRuntimeEventReadModelDiagnostic, projectRuntimeEventUserMessage, } from '@maka/runtime/runtime-event-read-model';
|
|
24
25
|
import { foldTurnContribution } from '@maka/storage/session-message-projection';
|
|
25
26
|
import { SESSION_TRANSCRIPT_OVERLAY_MAX_MESSAGES } from '../protocol/index.js';
|
|
26
27
|
const PERMISSION_OUTCOME_READ_CONCURRENCY = 8;
|
|
@@ -28,15 +29,13 @@ const PERMISSION_OUTCOME_READ_CONCURRENCY = 8;
|
|
|
28
29
|
const EVENT_SEQUENCE_STRIDE = 8;
|
|
29
30
|
export const ACTIVE_TRANSCRIPT_OVERLAY_MAX_MESSAGES = SESSION_TRANSCRIPT_OVERLAY_MAX_MESSAGES;
|
|
30
31
|
export const ACTIVE_TRANSCRIPT_OVERLAY_MAX_BYTES = 16 * 1024 * 1024;
|
|
31
|
-
const
|
|
32
|
+
const TRANSCRIPT_SOURCE_MAX_EVENTS = ACTIVE_TRANSCRIPT_OVERLAY_MAX_MESSAGES * 2;
|
|
33
|
+
// One RuntimeEvent can carry both the raw Tool Result and its durable model projection.
|
|
34
|
+
const TRANSCRIPT_SOURCE_MAX_RECORD_BYTES = DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES * 2 + 256 * 1024;
|
|
35
|
+
// The scan releases each decoded record after projection. Its memory budget is
|
|
36
|
+
// separate from the bounded amount of immutable input it may visit.
|
|
37
|
+
const TRANSCRIPT_SOURCE_MAX_BYTES = TRANSCRIPT_SOURCE_MAX_EVENTS * TRANSCRIPT_SOURCE_MAX_RECORD_BYTES;
|
|
32
38
|
const ACTIVE_TRANSCRIPT_SCAN_BATCH_MAX_BYTES = 256 * 1024;
|
|
33
|
-
/**
|
|
34
|
-
* What one durable page may read of a Turn, matching the bound the live
|
|
35
|
-
* overlay already holds for a run. A Turn past it is refused rather than
|
|
36
|
-
* half-projected: a prefix of a Turn is not a smaller transcript of it.
|
|
37
|
-
*/
|
|
38
|
-
const DURABLE_TRANSCRIPT_TURN_MAX_EVENTS = ACTIVE_TRANSCRIPT_SOURCE_MAX_EVENTS;
|
|
39
|
-
const DURABLE_TRANSCRIPT_TURN_MAX_BYTES = ACTIVE_TRANSCRIPT_OVERLAY_MAX_BYTES;
|
|
40
39
|
/** Turns per storage round trip: one, so a page loads no Turn it cannot use. */
|
|
41
40
|
const TRANSCRIPT_TURN_SCAN_LIMIT = 1;
|
|
42
41
|
/**
|
|
@@ -66,17 +65,7 @@ export function createSessionTranscriptReader(input) {
|
|
|
66
65
|
const root = await readRunInvocation(store, sessionId, rootTurn.runId);
|
|
67
66
|
if (!root)
|
|
68
67
|
return [];
|
|
69
|
-
const budget = { events: 0, bytes: 0 };
|
|
70
|
-
const scanned = new Map();
|
|
71
68
|
const invocations = new Map([[root.runId, root]]);
|
|
72
|
-
const readEvents = async (runId) => {
|
|
73
|
-
const cached = scanned.get(runId);
|
|
74
|
-
if (cached)
|
|
75
|
-
return cached;
|
|
76
|
-
const events = await readActiveRuntimeEvents(input.stores, sessionId, runId, budget);
|
|
77
|
-
scanned.set(runId, events);
|
|
78
|
-
return events;
|
|
79
|
-
};
|
|
80
69
|
let runIds = [root.runId];
|
|
81
70
|
if (root.terminalEvent && runtimeHandoffPause(root.terminalEvent)) {
|
|
82
71
|
const logical = await readLogicalRuntimeExecution({
|
|
@@ -87,26 +76,20 @@ export function createSessionTranscriptReader(input) {
|
|
|
87
76
|
invocations.set(runId, run);
|
|
88
77
|
return run;
|
|
89
78
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
}, { sessionId, turnId: rootTurn.turnId, runId: rootTurn.runId }, root);
|
|
79
|
+
readImmutableRuntimePrefixProof: (prefix) => store.readImmutableRuntimePrefixProof(prefix, {
|
|
80
|
+
maxEvents: TRANSCRIPT_SOURCE_MAX_EVENTS,
|
|
81
|
+
maxBytes: TRANSCRIPT_SOURCE_MAX_BYTES,
|
|
82
|
+
maxRecordBytes: TRANSCRIPT_SOURCE_MAX_RECORD_BYTES,
|
|
83
|
+
}),
|
|
84
|
+
}, { sessionId, turnId: rootTurn.turnId, runId: rootTurn.runId }, root, { mode: 'membership' });
|
|
98
85
|
if (!logical)
|
|
99
86
|
return [];
|
|
100
87
|
runIds = logical.runIds;
|
|
101
88
|
}
|
|
102
|
-
const
|
|
89
|
+
const pending = createTranscriptProjection(runIds.map((runId) => invocations.get(runId)), true);
|
|
103
90
|
for (const runId of runIds)
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
const projected = projectRuntimeEventsToStoredMessages(activePresentationRuntimeEvents(events), {
|
|
107
|
-
invocations: runIds.map((runId) => invocations.get(runId)),
|
|
108
|
-
canonicalPermissionOutcomes,
|
|
109
|
-
});
|
|
91
|
+
await scanActiveRuntimeEvents(input.stores, sessionId, runId, pending.push);
|
|
92
|
+
const projected = await pending.finish(input.canonicalPermissionOutcomes);
|
|
110
93
|
if (projected.diagnostics.some(isHardRuntimeEventReadModelDiagnostic)) {
|
|
111
94
|
throw new Error('Active RuntimeEvent transcript projection is incomplete');
|
|
112
95
|
}
|
|
@@ -131,11 +114,7 @@ function createDurableLedgerTranscriptReader(input) {
|
|
|
131
114
|
};
|
|
132
115
|
/** One Turn's rows, each at the sequence its own event sits at. */
|
|
133
116
|
const projectTurn = async (turn) => {
|
|
134
|
-
const
|
|
135
|
-
const projected = projectRuntimeEventsToStoredMessages(events, {
|
|
136
|
-
invocations: [turn.invocation],
|
|
137
|
-
canonicalPermissionOutcomes: await readCanonicalPermissionOutcomes(events, input.canonicalPermissionOutcomes),
|
|
138
|
-
});
|
|
117
|
+
const projected = await turn.projection.finish(input.canonicalPermissionOutcomes);
|
|
139
118
|
if (projected.diagnostics.some(isHardRuntimeEventReadModelDiagnostic)) {
|
|
140
119
|
throw new Error('Durable RuntimeEvent transcript projection is incomplete');
|
|
141
120
|
}
|
|
@@ -145,7 +124,7 @@ function createDurableLedgerTranscriptReader(input) {
|
|
|
145
124
|
const actionId = admission?.execution.kind === 'workhub_coordination'
|
|
146
125
|
? admission.execution.actionId
|
|
147
126
|
: undefined;
|
|
148
|
-
const ordinals =
|
|
127
|
+
const ordinals = turn.ordinals;
|
|
149
128
|
const emitted = new Map();
|
|
150
129
|
return projected.messages.map((message, index) => {
|
|
151
130
|
const ordinal = ordinals.get(projected.sourceEventIds[index]);
|
|
@@ -165,11 +144,20 @@ function createDurableLedgerTranscriptReader(input) {
|
|
|
165
144
|
};
|
|
166
145
|
});
|
|
167
146
|
};
|
|
168
|
-
const readTurns = async (sessionId, request) => store.readTranscriptInvocations(sessionId, {
|
|
147
|
+
const readTurns = async (sessionId, request, limit = TRANSCRIPT_TURN_SCAN_LIMIT) => store.readTranscriptInvocations(sessionId, {
|
|
169
148
|
...request,
|
|
170
|
-
limit
|
|
171
|
-
maxEvents:
|
|
172
|
-
maxBytes:
|
|
149
|
+
limit,
|
|
150
|
+
maxEvents: TRANSCRIPT_SOURCE_MAX_EVENTS,
|
|
151
|
+
maxBytes: TRANSCRIPT_SOURCE_MAX_BYTES,
|
|
152
|
+
maxRecordBytes: TRANSCRIPT_SOURCE_MAX_RECORD_BYTES,
|
|
153
|
+
}, (turn, events) => {
|
|
154
|
+
const projection = createTranscriptProjection([turn.invocation]);
|
|
155
|
+
const ordinals = new Map();
|
|
156
|
+
for (const { event, ordinal } of events) {
|
|
157
|
+
ordinals.set(event.id, ordinal);
|
|
158
|
+
projection.push(event);
|
|
159
|
+
}
|
|
160
|
+
return { ...turn, projection, ordinals };
|
|
173
161
|
});
|
|
174
162
|
const scan = async function* (sessionId, request) {
|
|
175
163
|
const throughSequence = request.throughSequence === undefined ? await highWater(sessionId) : request.throughSequence;
|
|
@@ -234,14 +222,11 @@ function createDurableLedgerTranscriptReader(input) {
|
|
|
234
222
|
if (watermark === null) {
|
|
235
223
|
return { throughSequence: null, contributions: [], nextPosition: null };
|
|
236
224
|
}
|
|
237
|
-
const turns = await
|
|
225
|
+
const turns = await readTurns(sessionId, {
|
|
238
226
|
direction: 'newer',
|
|
239
227
|
throughOrdinal: ordinalOf(watermark),
|
|
240
228
|
position: ordinalOf(position),
|
|
241
|
-
|
|
242
|
-
maxEvents: DURABLE_TRANSCRIPT_TURN_MAX_EVENTS,
|
|
243
|
-
maxBytes: DURABLE_TRANSCRIPT_TURN_MAX_BYTES,
|
|
244
|
-
});
|
|
229
|
+
}, maxContributions + 1);
|
|
245
230
|
const contributions = [];
|
|
246
231
|
for (const turn of turns.slice(0, maxContributions)) {
|
|
247
232
|
// Folded from the Turn's own rows, so `firstSequence` lands on its first
|
|
@@ -414,11 +399,7 @@ function assertActiveOverlayBounded(messages) {
|
|
|
414
399
|
}
|
|
415
400
|
}
|
|
416
401
|
}
|
|
417
|
-
async function readCanonicalPermissionOutcomes(
|
|
418
|
-
const requestIds = new Set(events.flatMap((event) => {
|
|
419
|
-
const requestId = event.actions?.permissionAnswerAccepted?.requestId;
|
|
420
|
-
return requestId ? [requestId] : [];
|
|
421
|
-
}));
|
|
402
|
+
async function readCanonicalPermissionOutcomes(requestIds, reader) {
|
|
422
403
|
const outcomes = new Map();
|
|
423
404
|
const ids = [...requestIds];
|
|
424
405
|
let encodedBytes = 0;
|
|
@@ -439,32 +420,67 @@ async function readCanonicalPermissionOutcomes(events, reader) {
|
|
|
439
420
|
}
|
|
440
421
|
return outcomes;
|
|
441
422
|
}
|
|
442
|
-
async function
|
|
443
|
-
const events = [];
|
|
423
|
+
async function scanActiveRuntimeEvents(stores, sessionId, runId, visit) {
|
|
444
424
|
const result = await stores.runtimeEventStore.scanRuntimeEvents(sessionId, runId, {
|
|
445
425
|
maxBatchBytes: ACTIVE_TRANSCRIPT_SCAN_BATCH_MAX_BYTES,
|
|
446
|
-
maxRecordBytes:
|
|
447
|
-
maxImmutableRecords:
|
|
448
|
-
maxImmutableBytes:
|
|
449
|
-
maxPartialRecords:
|
|
426
|
+
maxRecordBytes: TRANSCRIPT_SOURCE_MAX_RECORD_BYTES,
|
|
427
|
+
maxImmutableRecords: TRANSCRIPT_SOURCE_MAX_EVENTS,
|
|
428
|
+
maxImmutableBytes: TRANSCRIPT_SOURCE_MAX_BYTES,
|
|
429
|
+
maxPartialRecords: TRANSCRIPT_SOURCE_MAX_EVENTS,
|
|
450
430
|
maxPartialBytes: ACTIVE_TRANSCRIPT_OVERLAY_MAX_BYTES,
|
|
451
431
|
}, (batch) => {
|
|
452
|
-
for (const event of batch)
|
|
453
|
-
|
|
454
|
-
budget.bytes += Buffer.byteLength(JSON.stringify(event), 'utf8');
|
|
455
|
-
if (budget.events > ACTIVE_TRANSCRIPT_SOURCE_MAX_EVENTS) {
|
|
456
|
-
throw new Error('Active RuntimeEvent transcript exceeds its event limit');
|
|
457
|
-
}
|
|
458
|
-
if (budget.bytes > ACTIVE_TRANSCRIPT_OVERLAY_MAX_BYTES) {
|
|
459
|
-
throw new Error('Active RuntimeEvent transcript exceeds its byte limit');
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
events.push(...batch);
|
|
432
|
+
for (const event of batch)
|
|
433
|
+
visit(event);
|
|
463
434
|
});
|
|
464
435
|
if (result.status === 'limit_exceeded') {
|
|
465
436
|
throw new Error('Active RuntimeEvent transcript exceeds its storage scan limit');
|
|
466
437
|
}
|
|
467
|
-
|
|
438
|
+
}
|
|
439
|
+
/** Keep only presentation state while the storage snapshot visits complete facts. */
|
|
440
|
+
function createTranscriptProjection(invocations, active = false) {
|
|
441
|
+
const canonicalPermissionOutcomes = new Map();
|
|
442
|
+
let messageCount = 0;
|
|
443
|
+
let messageBytes = 0;
|
|
444
|
+
let eventCount = 0;
|
|
445
|
+
let sourceBytes = 0;
|
|
446
|
+
const projector = createRuntimeEventStoredMessageProjector({
|
|
447
|
+
invocations,
|
|
448
|
+
active,
|
|
449
|
+
canonicalPermissionOutcomes,
|
|
450
|
+
projectToolResult: projectTranscriptToolResult,
|
|
451
|
+
onMessage: (message) => {
|
|
452
|
+
messageCount += 1;
|
|
453
|
+
messageBytes += Buffer.byteLength(JSON.stringify(message));
|
|
454
|
+
if (messageCount > ACTIVE_TRANSCRIPT_OVERLAY_MAX_MESSAGES ||
|
|
455
|
+
messageBytes > ACTIVE_TRANSCRIPT_OVERLAY_MAX_BYTES)
|
|
456
|
+
throw new Error('Session transcript projection exceeds its presentation limit');
|
|
457
|
+
},
|
|
458
|
+
});
|
|
459
|
+
return {
|
|
460
|
+
push(event) {
|
|
461
|
+
eventCount += 1;
|
|
462
|
+
const content = event.content;
|
|
463
|
+
// The durable model projection is never a transcript input. Large Bash
|
|
464
|
+
// streams are consumed into a bounded terminal preview by the read model.
|
|
465
|
+
const measured = content?.kind === 'function_response'
|
|
466
|
+
? { ...event, content: { ...content, modelProjection: undefined, result: undefined } }
|
|
467
|
+
: event;
|
|
468
|
+
sourceBytes += Buffer.byteLength(JSON.stringify(measured));
|
|
469
|
+
if (eventCount > TRANSCRIPT_SOURCE_MAX_EVENTS)
|
|
470
|
+
throw new Error('Active RuntimeEvent transcript exceeds its event limit');
|
|
471
|
+
if (sourceBytes > ACTIVE_TRANSCRIPT_OVERLAY_MAX_BYTES)
|
|
472
|
+
throw new Error('Active RuntimeEvent transcript exceeds its byte limit');
|
|
473
|
+
projector.push(event);
|
|
474
|
+
},
|
|
475
|
+
async finish(reader) {
|
|
476
|
+
const outcomes = await readCanonicalPermissionOutcomes(projector.permissionRequestIds, reader);
|
|
477
|
+
for (const [id, outcome] of outcomes)
|
|
478
|
+
canonicalPermissionOutcomes.set(id, outcome);
|
|
479
|
+
const projected = projector.finish();
|
|
480
|
+
assertActiveOverlayBounded(projected.messages);
|
|
481
|
+
return projected;
|
|
482
|
+
},
|
|
483
|
+
};
|
|
468
484
|
}
|
|
469
485
|
function isTerminalTurn(turn) {
|
|
470
486
|
return turn.status === 'completed' || turn.status === 'failed' || turn.status === 'cancelled';
|
|
@@ -213,15 +213,31 @@ export class WorkHubCoordinationActionGate {
|
|
|
213
213
|
return this.#replace(intent, context);
|
|
214
214
|
}
|
|
215
215
|
const candidates = await this.candidates();
|
|
216
|
-
if (candidates.candidateSetId !== input.candidateSetId) {
|
|
216
|
+
if (!input.selectedTarget && candidates.candidateSetId !== input.candidateSetId) {
|
|
217
217
|
throw new WorkHubActionGateFailure('candidate_set_stale', 'WorkHub Session candidates changed; refresh before delegating');
|
|
218
218
|
}
|
|
219
|
-
const target = candidates.candidates.find((candidate) =>
|
|
219
|
+
const target = candidates.candidates.find((candidate) => input.selectedTarget
|
|
220
|
+
? candidate.sessionId === input.selectedTarget.sessionId &&
|
|
221
|
+
digest(candidate.workspace) === input.selectedTarget.workspaceDigest
|
|
222
|
+
: candidate.candidateRef === proposal.candidateRef);
|
|
220
223
|
if (!target) {
|
|
221
|
-
throw new WorkHubActionGateFailure('candidate_unavailable', 'WorkHub target is
|
|
224
|
+
throw new WorkHubActionGateFailure(input.selectedTarget ? 'candidate_set_stale' : 'candidate_unavailable', 'WorkHub target is no longer in the offered workspace');
|
|
222
225
|
}
|
|
223
226
|
this.#assertTarget(target);
|
|
224
|
-
return this.#assign(
|
|
227
|
+
return this.#assign({
|
|
228
|
+
...delegationAssignment(input, fingerprint, target.sessionId, target.sessionName),
|
|
229
|
+
...(input.selectedTarget
|
|
230
|
+
? {
|
|
231
|
+
validateFreshTarget: async () => {
|
|
232
|
+
const fresh = (await this.candidates()).candidates.find((candidate) => candidate.sessionId === input.selectedTarget.sessionId &&
|
|
233
|
+
digest(candidate.workspace) === input.selectedTarget.workspaceDigest);
|
|
234
|
+
if (!fresh)
|
|
235
|
+
throw new WorkHubActionGateFailure('candidate_set_stale', 'The selected work changed before admission');
|
|
236
|
+
this.#assertTarget(fresh);
|
|
237
|
+
},
|
|
238
|
+
}
|
|
239
|
+
: {}),
|
|
240
|
+
}, context);
|
|
225
241
|
}
|
|
226
242
|
/**
|
|
227
243
|
* The delegation a stop names, by the only two keys that can name it.
|
|
@@ -639,6 +655,7 @@ function digest(value) {
|
|
|
639
655
|
}
|
|
640
656
|
function actionFingerprint(input) {
|
|
641
657
|
const common = {
|
|
658
|
+
...(input.selectedTarget ? { selectedTarget: input.selectedTarget } : {}),
|
|
642
659
|
userText: input.userText,
|
|
643
660
|
...(input.attachments ? { attachments: input.attachments } : {}),
|
|
644
661
|
...(input.newWorkDefaults ? { newWorkDefaults: input.newWorkDefaults } : {}),
|
|
@@ -24,6 +24,7 @@ import { normalizeMessageContent } from '@maka/core/events';
|
|
|
24
24
|
import { WORKHUB_COORDINATION_SESSION_ID, WORKHUB_COORDINATION_SESSION_ROLE, WORKHUB_COORDINATION_REPLACEMENT_SCHEMA_VERSION, WORKHUB_COORDINATION_STOP_SCHEMA_VERSION, isWorkHubCoordinationSession, isWorkHubCoordinationSessionId, } from '@maka/core/session';
|
|
25
25
|
import { WORKHUB_COORDINATION_TEXT_MAX_BYTES } from '../protocol/index.js';
|
|
26
26
|
import { SessionOperationFailure, projectSessionCatalogRecord, } from './session-catalog-coordinator.js';
|
|
27
|
+
import { RuntimeInteractionAdmissionRejectedError, RuntimeInteractionFailStopError, } from '@maka/runtime/interaction-authority';
|
|
27
28
|
import { WorkHubActionEffectFailure, WorkHubActionGateFailure, WorkHubCoordinationActionGate, } from './workhub-coordination-action-gate.js';
|
|
28
29
|
const CREATE_FINGERPRINT = `sha256:${createHash('sha256')
|
|
29
30
|
.update('maka:workhub-coordination-session:v1', 'utf8')
|
|
@@ -44,7 +45,9 @@ export const WORKHUB_ROUTING_HISTORY_MAX_MESSAGES = 32;
|
|
|
44
45
|
export const WORKHUB_ROUTING_HISTORY_MAX_STORED_BYTES = 64 * 1024;
|
|
45
46
|
/** Resolves the one durable Coordination Session owned by this Runtime Host. */
|
|
46
47
|
export class HostWorkHubCoordinationCoordinator {
|
|
48
|
+
#requestForm;
|
|
47
49
|
handlers = {
|
|
50
|
+
'workhub.coordination.selectAndDelegate': (input, context) => this.#selectAndDelegate(input, context),
|
|
48
51
|
'workhub.coordination.resolve': () => this.#resolve(),
|
|
49
52
|
'workhub.coordination.query': () => this.#query(),
|
|
50
53
|
'workhub.coordination.configureModel': (input) => this.#configureModel(input),
|
|
@@ -65,6 +68,7 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
65
68
|
#routingModel;
|
|
66
69
|
#readDelegationRetirement;
|
|
67
70
|
constructor(options) {
|
|
71
|
+
this.#requestForm = options.requestForm;
|
|
68
72
|
this.#configureModel = options.configureModel;
|
|
69
73
|
this.#routingModel = options.routingModel;
|
|
70
74
|
this.#transitionConfiguration = options.transitionConfiguration;
|
|
@@ -332,7 +336,146 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
332
336
|
};
|
|
333
337
|
}
|
|
334
338
|
}
|
|
335
|
-
async #
|
|
339
|
+
async #selectAndDelegate(input, context) {
|
|
340
|
+
try {
|
|
341
|
+
const authority = await this.#executions.readActiveWorkHubRoutingRequest(input.turnId);
|
|
342
|
+
if (!authority || authority.decision || !this.#requestForm) {
|
|
343
|
+
return {
|
|
344
|
+
ok: false,
|
|
345
|
+
error: {
|
|
346
|
+
code: 'operation_conflict',
|
|
347
|
+
message: 'Target selection requires an active, unbound Coordination Turn',
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
// The interaction identity binds the complete operation and original user authority.
|
|
352
|
+
// Its durable answer can be replayed without rebuilding (or interpreting) the offer.
|
|
353
|
+
const requestId = createHash('sha256')
|
|
354
|
+
.update(JSON.stringify({
|
|
355
|
+
input,
|
|
356
|
+
runId: authority.runId,
|
|
357
|
+
content: authority.content,
|
|
358
|
+
}))
|
|
359
|
+
.digest('hex');
|
|
360
|
+
const choice = await this.#requestForm({
|
|
361
|
+
sessionId: WORKHUB_COORDINATION_SESSION_ID,
|
|
362
|
+
turnId: input.turnId,
|
|
363
|
+
runId: authority.runId,
|
|
364
|
+
requestId,
|
|
365
|
+
create: async () => {
|
|
366
|
+
if (await this.#stores.readWorkHubAssignment(input.actionId)) {
|
|
367
|
+
throw new WorkHubActionGateFailure('action_conflict', 'This action identity already belongs to another selection');
|
|
368
|
+
}
|
|
369
|
+
const page = await this.#actionGate.candidates();
|
|
370
|
+
if (page.candidateSetId !== input.candidateSetId) {
|
|
371
|
+
throw new WorkHubActionGateFailure('candidate_set_stale', 'Discover fresh candidates before requesting a target choice');
|
|
372
|
+
}
|
|
373
|
+
const options = input.candidateRefs.map((ref) => {
|
|
374
|
+
const candidate = page.candidates.find((item) => item.candidateRef === ref);
|
|
375
|
+
if (!candidate)
|
|
376
|
+
throw new WorkHubActionGateFailure('candidate_unavailable', 'Target choice contains an unavailable candidate');
|
|
377
|
+
// Keep labels inside the shared form wire budget; identity is the opaque value.
|
|
378
|
+
const label = `${candidate.sessionName} — ${candidate.workspace.hostCwd}`;
|
|
379
|
+
let bounded = '';
|
|
380
|
+
for (const character of label) {
|
|
381
|
+
if (Buffer.byteLength(bounded + character, 'utf8') > 190)
|
|
382
|
+
break;
|
|
383
|
+
bounded += character;
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
// An opaque, durable binding: only an exact offered value is accepted by
|
|
387
|
+
// the form authority. Names and model-written answers never resolve identity.
|
|
388
|
+
value: JSON.stringify([ref, candidate.sessionId, digest(candidate.workspace)]),
|
|
389
|
+
label: page.candidates.some((other) => other.sessionId !== candidate.sessionId &&
|
|
390
|
+
other.sessionName === candidate.sessionName &&
|
|
391
|
+
other.workspace.hostCwd === candidate.workspace.hostCwd)
|
|
392
|
+
? `${bounded} [${candidate.sessionId.slice(0, 12)}]`
|
|
393
|
+
: bounded,
|
|
394
|
+
};
|
|
395
|
+
});
|
|
396
|
+
return {
|
|
397
|
+
kind: 'form',
|
|
398
|
+
toolUseId: input.actionId,
|
|
399
|
+
message: 'Choose the work to continue',
|
|
400
|
+
requester: { name: 'WorkHub' },
|
|
401
|
+
fields: [
|
|
402
|
+
{
|
|
403
|
+
kind: 'single_select',
|
|
404
|
+
name: 'target',
|
|
405
|
+
label: 'Work / Workspace',
|
|
406
|
+
required: true,
|
|
407
|
+
options,
|
|
408
|
+
},
|
|
409
|
+
],
|
|
410
|
+
};
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
if (choice.answer.action !== 'accept')
|
|
414
|
+
return { ok: true, result: { kind: 'cancelled' } };
|
|
415
|
+
const value = choice.answer.values.target;
|
|
416
|
+
const binding = typeof value === 'string' ? JSON.parse(value) : undefined;
|
|
417
|
+
if (!Array.isArray(binding) ||
|
|
418
|
+
binding.length !== 3 ||
|
|
419
|
+
!binding.every((part) => typeof part === 'string') ||
|
|
420
|
+
!input.candidateRefs.includes(binding[0])) {
|
|
421
|
+
return {
|
|
422
|
+
ok: false,
|
|
423
|
+
error: {
|
|
424
|
+
code: 'operation_conflict',
|
|
425
|
+
message: 'Target choice does not belong to this operation',
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
if (Date.now() - choice.createdAt > 10 * 60_000 &&
|
|
430
|
+
!(await this.#stores.readWorkHubAssignment(input.actionId))) {
|
|
431
|
+
return {
|
|
432
|
+
ok: false,
|
|
433
|
+
error: {
|
|
434
|
+
code: 'candidate_set_stale',
|
|
435
|
+
message: 'Target choice expired; discover candidates and ask again',
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
const [offeredRef, sessionId, workspaceDigest] = binding;
|
|
440
|
+
const freshAuthority = await this.#executions.readActiveWorkHubRoutingRequest(input.turnId);
|
|
441
|
+
if (freshAuthority?.runId !== authority.runId) {
|
|
442
|
+
return {
|
|
443
|
+
ok: false,
|
|
444
|
+
error: { code: 'operation_conflict', message: 'The selecting Run is no longer active' },
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
// Re-read active authority after waiting. The Gate performs fresh candidate validation
|
|
448
|
+
// and durable action replay before it admits any target execution.
|
|
449
|
+
const outcome = await this.#actFromTurn({
|
|
450
|
+
turnId: input.turnId,
|
|
451
|
+
actionId: input.actionId,
|
|
452
|
+
proposal: { disposition: 'delegate_existing', candidateRef: offeredRef },
|
|
453
|
+
candidateSetId: input.candidateSetId,
|
|
454
|
+
delegationText: input.delegationText,
|
|
455
|
+
}, context, { sessionId, workspaceDigest });
|
|
456
|
+
return outcome.ok
|
|
457
|
+
? { ok: true, result: { kind: 'delegated', result: outcome.result } }
|
|
458
|
+
: outcome;
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
if (error instanceof RuntimeInteractionFailStopError)
|
|
462
|
+
throw error;
|
|
463
|
+
return {
|
|
464
|
+
ok: false,
|
|
465
|
+
error: {
|
|
466
|
+
code: error instanceof WorkHubActionGateFailure
|
|
467
|
+
? error.code === 'candidate_set_stale'
|
|
468
|
+
? 'candidate_set_stale'
|
|
469
|
+
: 'operation_conflict'
|
|
470
|
+
: error instanceof RuntimeInteractionAdmissionRejectedError
|
|
471
|
+
? 'operation_conflict'
|
|
472
|
+
: 'persistence_failed',
|
|
473
|
+
message: error instanceof Error ? error.message : 'Target selection is unavailable',
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async #actFromTurn(input, context, selectedTarget) {
|
|
336
479
|
let request;
|
|
337
480
|
try {
|
|
338
481
|
request = await this.#executions.readActiveWorkHubRoutingRequest(input.turnId);
|
|
@@ -372,6 +515,7 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
372
515
|
ok: true,
|
|
373
516
|
result: await this.#actionGate.act({
|
|
374
517
|
...action,
|
|
518
|
+
...(selectedTarget ? { selectedTarget } : {}),
|
|
375
519
|
userText: request.content.text,
|
|
376
520
|
...(carriesAttachments && request.content.attachments
|
|
377
521
|
? { attachments: request.content.attachments }
|
|
@@ -24,7 +24,7 @@ import { assertNoReservedWorkspaceAuthorityAppend } from './runtime-event-author
|
|
|
24
24
|
import { acquireOperationalStateDatabase, } from './operational-state-store.js';
|
|
25
25
|
import { assertEvidenceReadBudget, measureEvidenceRows, } from './bounded-evidence.js';
|
|
26
26
|
import { decodeSkillInvocationResult, } from '@maka/core/skill-invocation';
|
|
27
|
-
import { aggregateMessageContents, decodeMessageContent, isCanonicalAttachmentRef, messageContentsEqual, } from '@maka/core/events';
|
|
27
|
+
import { aggregateMessageContents, decodeMessageContent, hasMeaningfulMessageContent, isCanonicalAttachmentRef, messageContentsEqual, } from '@maka/core/events';
|
|
28
28
|
import { decodeAgentGraphIntentClaim } from '@maka/core/agent-graph-control';
|
|
29
29
|
import { MAX_ATTACHMENT_BYTES, MAX_ATTACHMENT_COUNT } from '@maka/core/attachments';
|
|
30
30
|
import { MODEL_CALL_ATTEMPT_EVENT_TYPE } from '@maka/core/model-call-attempt';
|
|
@@ -1052,7 +1052,10 @@ function normalizeRootTurnMessageContent(value, description, maxAttachments) {
|
|
|
1052
1052
|
}
|
|
1053
1053
|
throw new Error(`Invalid ${description}`);
|
|
1054
1054
|
}
|
|
1055
|
-
|
|
1055
|
+
// Quote- or attachment-only input is meaningful (#4804): the text carrier
|
|
1056
|
+
// alone no longer decides durability admission.
|
|
1057
|
+
if (!hasMeaningfulMessageContent(normalized) ||
|
|
1058
|
+
(normalized.attachments?.length ?? 0) > maxAttachments) {
|
|
1056
1059
|
throw new Error(`Invalid ${description}`);
|
|
1057
1060
|
}
|
|
1058
1061
|
for (const [index, attachment] of (normalized.attachments ?? []).entries()) {
|
|
@@ -229,6 +229,7 @@ async function createExecutionStoresForWrite(lease, kind, extension) {
|
|
|
229
229
|
readRuntimeEventsBounded: (sessionId, runId, budget) => run(() => runtimeEventStore.readRuntimeEventsBounded(sessionId, runId, budget)),
|
|
230
230
|
readImmutableRuntimeEvents: (sessionId, runId) => run(() => runtimeEventStore.readImmutableRuntimeEvents(sessionId, runId)),
|
|
231
231
|
readImmutableRuntimePrefix: (input) => run(() => runtimeEventStore.readImmutableRuntimePrefix(input)),
|
|
232
|
+
readImmutableRuntimePrefixProof: (input, budget) => run(() => runtimeEventStore.readImmutableRuntimePrefixProof(input, budget)),
|
|
232
233
|
listSessionInvocations: (sessionId) => run(() => runtimeEventStore.listSessionInvocations(sessionId)),
|
|
233
234
|
readRunInvocation: (sessionId, runId) => run(() => runtimeEventStore.readRunInvocation(sessionId, runId)),
|
|
234
235
|
listSessionInvocationsBounded: (sessionId, limit) => run(() => runtimeEventStore.listSessionInvocationsBounded(sessionId, limit)),
|
|
@@ -238,7 +239,7 @@ async function createExecutionStoresForWrite(lease, kind, extension) {
|
|
|
238
239
|
readSessionRuntimeEventEntries: (sessionId) => run(() => runtimeEventStore.readSessionRuntimeEventEntries(sessionId)),
|
|
239
240
|
resequenceSessionEventOrdinals: (sessionId) => run(() => runtimeEventStore.resequenceSessionEventOrdinals(sessionId)),
|
|
240
241
|
readTranscriptHighWater: (sessionId) => run(() => runtimeEventStore.readTranscriptHighWater(sessionId)),
|
|
241
|
-
readTranscriptInvocations: (sessionId, request) => run(() => runtimeEventStore.readTranscriptInvocations(sessionId, request)),
|
|
242
|
+
readTranscriptInvocations: (sessionId, request, project) => run(() => runtimeEventStore.readTranscriptInvocations(sessionId, request, project)),
|
|
242
243
|
readTranscriptLandmarks: (sessionId, throughOrdinal, limit) => run(() => runtimeEventStore.readTranscriptLandmarks(sessionId, throughOrdinal, limit)),
|
|
243
244
|
claimContinuation: (input) => run(() => runtimeEventStore.claimContinuation(input)),
|
|
244
245
|
readContinuationClaimByBoundary: (boundaryDigest) => run(() => runtimeEventStore.readContinuationClaimByBoundary(boundaryDigest)),
|
|
@@ -107,9 +107,12 @@ export class RuntimeTranscriptQuery {
|
|
|
107
107
|
});
|
|
108
108
|
return row?.last ?? null;
|
|
109
109
|
}
|
|
110
|
-
invocations(sessionId, request) {
|
|
110
|
+
invocations(sessionId, request, project) {
|
|
111
111
|
assertOrdinal(request.throughOrdinal);
|
|
112
112
|
assertOrdinal(request.position);
|
|
113
|
+
assertReadLimit(request.maxEvents, 'event count');
|
|
114
|
+
assertReadLimit(request.maxBytes, 'byte');
|
|
115
|
+
assertReadLimit(request.maxRecordBytes, 'record byte');
|
|
113
116
|
if (request.direction !== 'older' && request.direction !== 'newer') {
|
|
114
117
|
throw new Error('Invalid transcript direction');
|
|
115
118
|
}
|
|
@@ -130,12 +133,11 @@ export class RuntimeTranscriptQuery {
|
|
|
130
133
|
throughOrdinal: request.throughOrdinal,
|
|
131
134
|
limit: request.limit,
|
|
132
135
|
}).sort((a, b) => a.first - b.first);
|
|
133
|
-
return rows.map((row) => ({
|
|
136
|
+
return rows.map((row) => project({
|
|
134
137
|
invocation: this.invocation(sessionId, row.invocation_id),
|
|
135
138
|
firstOrdinal: row.first,
|
|
136
139
|
lastOrdinal: row.last,
|
|
137
|
-
|
|
138
|
-
}));
|
|
140
|
+
}, this.events(row.invocation_id, request)));
|
|
139
141
|
}
|
|
140
142
|
landmarks(sessionId, throughOrdinal, limit) {
|
|
141
143
|
assertOrdinal(throughOrdinal);
|
|
@@ -258,29 +260,35 @@ export class RuntimeTranscriptQuery {
|
|
|
258
260
|
limit: bounds.limit,
|
|
259
261
|
});
|
|
260
262
|
}
|
|
261
|
-
events(invocationId, limits) {
|
|
262
|
-
// Walked row by row
|
|
263
|
-
//
|
|
263
|
+
*events(invocationId, limits) {
|
|
264
|
+
// Walked row by row so cumulative limits apply to raw IO without retaining
|
|
265
|
+
// the Turn. SQLite withholds an oversized payload before it crosses into JS.
|
|
264
266
|
const cursor = this.db
|
|
265
267
|
.prepare(`
|
|
266
|
-
SELECT o.ordinal, e.event_id, e.session_id, e.invocation_id, e.run_id, e.turn_id,
|
|
268
|
+
SELECT o.ordinal, e.event_id, e.session_id, e.invocation_id, e.run_id, e.turn_id,
|
|
269
|
+
length(CAST(e.payload_json AS BLOB)) AS stored_bytes,
|
|
270
|
+
CASE WHEN length(CAST(e.payload_json AS BLOB)) <= ? THEN e.payload_json END AS payload_json
|
|
267
271
|
FROM runtime_events e JOIN runtime_session_event_ordinals o ON o.event_id = e.event_id
|
|
268
272
|
WHERE e.invocation_id = ? ORDER BY e.event_seq
|
|
269
273
|
`)
|
|
270
|
-
.iterate(invocationId);
|
|
271
|
-
|
|
274
|
+
.iterate(limits.maxRecordBytes, invocationId);
|
|
275
|
+
let count = 0;
|
|
272
276
|
let bytes = 0;
|
|
273
277
|
for (const row of cursor) {
|
|
274
|
-
if (
|
|
278
|
+
if (count === limits.maxEvents) {
|
|
275
279
|
throw new RuntimeTranscriptOversizedTurnError(`Turn ${invocationId} holds more RuntimeEvents than a transcript page may read`);
|
|
276
280
|
}
|
|
277
|
-
|
|
281
|
+
const payload = row.payload_json;
|
|
282
|
+
if (payload === null) {
|
|
283
|
+
throw new RuntimeTranscriptOversizedTurnError(`Turn ${invocationId} holds a RuntimeEvent larger than a transcript page may read`);
|
|
284
|
+
}
|
|
285
|
+
bytes += row.stored_bytes;
|
|
278
286
|
if (bytes > limits.maxBytes) {
|
|
279
287
|
throw new RuntimeTranscriptOversizedTurnError(`Turn ${invocationId} holds more RuntimeEvent bytes than a transcript page may read`);
|
|
280
288
|
}
|
|
281
|
-
|
|
289
|
+
count += 1;
|
|
290
|
+
yield { ordinal: row.ordinal, event: decodeStoredEvent({ ...row, payload_json: payload }) };
|
|
282
291
|
}
|
|
283
|
-
return events;
|
|
284
292
|
}
|
|
285
293
|
event(id) {
|
|
286
294
|
const row = this.db
|
|
@@ -306,3 +314,8 @@ function assertOrdinal(value) {
|
|
|
306
314
|
if (!Number.isSafeInteger(value) || value < 0)
|
|
307
315
|
throw new Error('Invalid transcript event ordinal');
|
|
308
316
|
}
|
|
317
|
+
function assertReadLimit(value, name) {
|
|
318
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
319
|
+
throw new RangeError(`Invalid transcript ${name} limit`);
|
|
320
|
+
}
|
|
321
|
+
}
|