maka-agent 0.2.0-dev.12.20260901 → 0.2.0-dev.14.20260902
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 +2 -2
- package/dist/acp/maka-acp-agent.js +6 -3
- package/dist/acp/session-registry.js +307 -0
- package/dist/acp/stdio-server.js +20 -1
- package/dist/cli-core.js +15 -2
- package/dist/pi-transcript.js +1 -1
- package/dist/pi-tui-layout.js +1 -1
- package/dist/pi-tui-mcp-status.js +1 -1
- package/dist/pi-tui-runner.js +1 -1
- package/dist/runtime-host-access-command.js +37 -4
- package/dist/runtime-host-capability-provider-command.js +16 -2
- package/dist/runtime-host-cli-context.js +73 -3
- package/dist/runtime-host-cli.js +37 -6
- package/dist/runtime-host-run-command.js +3 -1
- package/dist/runtime-host-update-command.js +59 -31
- package/dist/tui-copy-catalog.js +4 -2
- package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/node_modules/@maka/core/dist/attachments.js +12 -0
- package/node_modules/@maka/core/dist/connection-error-copy.js +17 -34
- package/node_modules/@maka/core/dist/events.js +0 -13
- package/node_modules/@maka/core/dist/maka-wordmark.js +23 -0
- package/node_modules/@maka/core/dist/model-catalog.js +22 -1
- package/node_modules/@maka/core/dist/runtime-event.js +1 -0
- package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +50 -1
- package/node_modules/@maka/core/dist/runtime-policy/credential-vault-codec.js +9 -1
- package/node_modules/@maka/core/dist/runtime-policy/model-catalog-entry-codec.js +10 -1
- package/node_modules/@maka/core/dist/runtime-policy/policy-codec.js +75 -0
- package/node_modules/@maka/core/dist/runtime-policy.js +4 -2
- package/node_modules/@maka/core/dist/session.js +1 -0
- package/node_modules/@maka/core/dist/settings.js +10 -2
- package/node_modules/@maka/core/dist/usage-record-schema.js +11 -1
- package/node_modules/@maka/core/package.json +2 -1
- package/node_modules/@maka/mcp/dist/index.js +1 -0
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +52 -80
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +178 -140
- package/node_modules/@maka/runtime/dist/archive-read-tool.js +26 -5
- package/node_modules/@maka/runtime/dist/builtin-tools.js +27 -2
- package/node_modules/@maka/runtime/dist/context-budget-policy.js +0 -12
- package/node_modules/@maka/runtime/dist/conversation-copy.js +66 -43
- package/node_modules/@maka/runtime/dist/durable-tool-result-projection.js +31 -0
- package/node_modules/@maka/runtime/dist/history-compact-error.js +10 -2
- package/node_modules/@maka/runtime/dist/history-compact-input-fit.js +7 -1
- package/node_modules/@maka/runtime/dist/history-compaction.js +13 -22
- package/node_modules/@maka/runtime/dist/model-history.js +41 -16
- package/node_modules/@maka/runtime/dist/provider-image-overflow-recovery.js +12 -31
- package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +3 -0
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +13 -2
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -6
- package/node_modules/@maka/runtime/dist/shell-tools.js +77 -1
- package/node_modules/@maka/runtime/dist/tool-availability.js +74 -5
- package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +333 -52
- package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +34 -3
- package/node_modules/@maka/runtime/dist/tool-runtime.js +12 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +0 -3
- package/node_modules/@maka/runtime-host/dist/client/catalog-reader.js +40 -13
- package/node_modules/@maka/runtime-host/dist/client/connection.js +50 -31
- package/node_modules/@maka/runtime-host/dist/client/host-profile.js +48 -4
- package/node_modules/@maka/runtime-host/dist/client/index.js +2 -2
- package/node_modules/@maka/runtime-host/dist/client/owner-connection-code.js +32 -1
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +22 -4
- package/node_modules/@maka/runtime-host/dist/operator/access-management-frame.js +10 -1
- package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +56 -12
- package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +3 -0
- package/node_modules/@maka/runtime-host/dist/protocol/configuration.js +55 -9
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +37 -0
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +17 -1
- package/node_modules/@maka/runtime-host/dist/protocol/network-proxy.js +1 -7
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +2 -0
- package/node_modules/@maka/runtime-host/dist/protocol/runtime-policy.js +48 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +4 -2
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +2 -12
- package/node_modules/@maka/runtime-host/dist/server/access-authority.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/agent-graph-execution-coordinator.js +1 -2
- package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +0 -9
- package/node_modules/@maka/runtime-host/dist/server/configuration-coordinator.js +20 -1
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +46 -24
- package/node_modules/@maka/runtime-host/dist/server/execution-service.js +4 -1
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +15 -0
- package/node_modules/@maka/runtime-host/dist/server/listener-set.js +8 -4
- package/node_modules/@maka/runtime-host/dist/server/network-proxy-coordinator.js +0 -1
- package/node_modules/@maka/runtime-host/dist/server/peer-listener.js +3 -0
- package/node_modules/@maka/runtime-host/dist/server/runtime-policy-coordinator.js +28 -1
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +3 -3
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +35 -3
- package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +16 -0
- package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +189 -6
- package/node_modules/@maka/storage/dist/runtime-policy-stores.js +4 -1
- package/node_modules/@maka/storage/dist/settings-store.js +16 -25
- package/node_modules/fast-uri/index.js +215 -47
- package/node_modules/fast-uri/lib/schemes.js +9 -4
- package/node_modules/fast-uri/lib/utils.js +396 -92
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
- package/node_modules/qs/CHANGELOG.md +15 -0
- package/node_modules/qs/README.md +19 -1
- package/node_modules/qs/dist/qs.js +21 -21
- package/node_modules/qs/eslint.config.mjs +1 -1
- package/node_modules/qs/lib/parse.js +3 -4
- package/node_modules/qs/lib/stringify.js +23 -8
- package/node_modules/qs/lib/utils.js +10 -3
- package/node_modules/qs/package.json +4 -4
- package/package.json +1 -1
|
@@ -27,28 +27,38 @@ export function buildArchiveReadTool(reader) {
|
|
|
27
27
|
.string()
|
|
28
28
|
.describe('A maka://archive/... ref returned in an archived tool-result placeholder'),
|
|
29
29
|
operation: z
|
|
30
|
-
.enum(['inspect', 'read', 'query'])
|
|
30
|
+
.enum(['inspect', 'read', 'query', 'search'])
|
|
31
31
|
.default('inspect')
|
|
32
|
-
.describe('inspect lists archive metadata/items; query reads one structured item; read returns one bounded
|
|
32
|
+
.describe('inspect lists archive metadata/items and a preview; query reads one structured item; read returns one bounded page (by character or line); search locates a substring'),
|
|
33
|
+
unit: z
|
|
34
|
+
.enum(['char', 'line'])
|
|
35
|
+
.default('char')
|
|
36
|
+
.describe('For read: whether offset/limit count characters (default) or whole lines'),
|
|
33
37
|
offset: z
|
|
34
38
|
.number()
|
|
35
39
|
.int()
|
|
36
40
|
.nonnegative()
|
|
37
41
|
.optional()
|
|
38
|
-
.describe('Zero-based character offset for read
|
|
42
|
+
.describe('Zero-based start for pagination: character or line offset for read (per unit), character offset for query, or the character position to resume search from'),
|
|
39
43
|
limit: z
|
|
40
44
|
.number()
|
|
41
45
|
.int()
|
|
42
46
|
.positive()
|
|
43
47
|
.max(TOOL_RESULT_ARCHIVE_MAX_LIMIT)
|
|
44
48
|
.optional()
|
|
45
|
-
.describe(`Maximum returned characters, capped at ${TOOL_RESULT_ARCHIVE_MAX_LIMIT}`),
|
|
49
|
+
.describe(`Maximum returned characters (or lines when unit is "line"), capped at ${TOOL_RESULT_ARCHIVE_MAX_LIMIT}`),
|
|
46
50
|
itemId: z
|
|
47
51
|
.string()
|
|
48
52
|
.min(1)
|
|
49
53
|
.max(256)
|
|
50
54
|
.optional()
|
|
51
55
|
.describe('Structured item id returned by inspect; required for query'),
|
|
56
|
+
pattern: z
|
|
57
|
+
.string()
|
|
58
|
+
.min(1)
|
|
59
|
+
.max(256)
|
|
60
|
+
.optional()
|
|
61
|
+
.describe('Literal, case-insensitive substring to locate; required for search'),
|
|
52
62
|
})
|
|
53
63
|
.strip()
|
|
54
64
|
.superRefine((value, ctx) => {
|
|
@@ -59,13 +69,20 @@ export function buildArchiveReadTool(reader) {
|
|
|
59
69
|
message: 'itemId is required for query',
|
|
60
70
|
});
|
|
61
71
|
}
|
|
72
|
+
if (value.operation === 'search' && !value.pattern) {
|
|
73
|
+
ctx.addIssue({
|
|
74
|
+
code: 'custom',
|
|
75
|
+
path: ['pattern'],
|
|
76
|
+
message: 'pattern is required for search',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
62
79
|
}));
|
|
63
80
|
const providerSchema = zodSchema(parameters);
|
|
64
81
|
return {
|
|
65
82
|
name: ARCHIVE_READ_TOOL_NAME,
|
|
66
83
|
displayName: 'Read archived result',
|
|
67
84
|
activityKind: 'read',
|
|
68
|
-
description: 'Inspect or page through a tool-result archive returned as a maka://archive/... ref. Start with inspect.
|
|
85
|
+
description: 'Inspect, search, or page through a tool-result archive returned as a maka://archive/... ref. Start with inspect for a preview and the char/line coordinate space. Use operation "search" with a pattern to locate text, operation "read" with unit "line" for line-oriented terminal output, or operation "query" with an itemId for one agent_swarm item. Results are strictly bounded so reading an archive cannot immediately trigger another archive.',
|
|
69
86
|
parameters: jsonSchema(async () => await providerSchema.jsonSchema, {
|
|
70
87
|
validate: async (value) => {
|
|
71
88
|
const result = await parameters.safeParseAsync(value);
|
|
@@ -84,6 +101,10 @@ function cleanArchiveReadInput(input) {
|
|
|
84
101
|
const operation = cleaned.operation ?? 'inspect';
|
|
85
102
|
if (operation !== 'query')
|
|
86
103
|
delete cleaned.itemId;
|
|
104
|
+
if (operation !== 'search')
|
|
105
|
+
delete cleaned.pattern;
|
|
106
|
+
if (operation !== 'read')
|
|
107
|
+
delete cleaned.unit;
|
|
87
108
|
if (operation === 'inspect') {
|
|
88
109
|
delete cleaned.offset;
|
|
89
110
|
delete cleaned.limit;
|
|
@@ -27,6 +27,7 @@ import { tmpdir } from 'node:os';
|
|
|
27
27
|
import { basename, dirname } from 'node:path';
|
|
28
28
|
import { compilePermissionProfile } from '@maka/core/permission-profile-compiler';
|
|
29
29
|
import { parseAttachmentResourceRef } from '@maka/core/attachments';
|
|
30
|
+
import { isStorageRef } from '@maka/core/events';
|
|
30
31
|
import { bashToolResultToModelOutput } from './bash-model-output.js';
|
|
31
32
|
import { fileWriteToolResultToModelOutput } from './file-tool-model-output.js';
|
|
32
33
|
import { openAiApplyPatchInputSchema } from './openai-apply-patch.js';
|
|
@@ -236,6 +237,28 @@ export function buildBuiltinTools(options = {}) {
|
|
|
236
237
|
description: readDescription,
|
|
237
238
|
parameters: readParameters,
|
|
238
239
|
executionFacts,
|
|
240
|
+
...(options.releaseImageSnapshot
|
|
241
|
+
? {
|
|
242
|
+
compensateDurableOutcomeCommitFailure: async (input) => {
|
|
243
|
+
const result = input.result;
|
|
244
|
+
if (!result ||
|
|
245
|
+
typeof result !== 'object' ||
|
|
246
|
+
result.kind !== 'image') {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const ref = result.ref;
|
|
250
|
+
if (!isStorageRef(ref) ||
|
|
251
|
+
ref.kind !== 'session_context' ||
|
|
252
|
+
ref.sessionId !== input.sessionId) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
await options.releaseImageSnapshot({
|
|
256
|
+
sessionId: ref.sessionId,
|
|
257
|
+
refId: ref.refId,
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
: {}),
|
|
239
262
|
impl: async (input, ctx) => {
|
|
240
263
|
const { cwd, sessionId, abortSignal } = ctx;
|
|
241
264
|
if ('ref' in input) {
|
|
@@ -273,10 +296,12 @@ export function buildBuiltinTools(options = {}) {
|
|
|
273
296
|
if (result.kind === 'read_image') {
|
|
274
297
|
if (!options.snapshotImage)
|
|
275
298
|
throw new Error('Read image snapshots are not available in this toolset.');
|
|
299
|
+
if (!ctx.operationId) {
|
|
300
|
+
throw new Error('Read image snapshots require a durable tool operation identity.');
|
|
301
|
+
}
|
|
276
302
|
const ref = await options.snapshotImage({
|
|
277
303
|
sessionId,
|
|
278
|
-
|
|
279
|
-
name: basename(path),
|
|
304
|
+
ownerId: ctx.operationId,
|
|
280
305
|
bytes: result.bytes,
|
|
281
306
|
mimeType: result.mimeType,
|
|
282
307
|
});
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { lookupModelMetadata } from '@maka/core/model-metadata';
|
|
20
20
|
import { relayModelProfile } from '@maka/core/model-thinking';
|
|
21
|
-
import { finitePositive } from './context-budget-helpers.js';
|
|
22
21
|
export function buildDefaultContextBudgetPolicy(connection, options = {}) {
|
|
23
22
|
const contextWindow = resolveSelectedModelContextWindow(connection, options.modelId);
|
|
24
23
|
const reserveTokens = defaultCompactReserveTokens(contextWindow);
|
|
@@ -98,14 +97,3 @@ function narrowestPositiveLimit(...values) {
|
|
|
98
97
|
const positiveValues = values.filter((value) => typeof value === 'number' && Number.isFinite(value) && value > 0);
|
|
99
98
|
return positiveValues.length > 0 ? Math.min(...positiveValues) : undefined;
|
|
100
99
|
}
|
|
101
|
-
export function resolveContextBudgetCapacity(connection, modelId, policy) {
|
|
102
|
-
const selectedWindow = resolveSelectedModelContextWindow(connection, modelId);
|
|
103
|
-
if (selectedWindow !== undefined) {
|
|
104
|
-
return { tokens: selectedWindow, source: 'selected_model' };
|
|
105
|
-
}
|
|
106
|
-
const historyBudget = finitePositive(policy?.maxHistoryEstimatedTokens);
|
|
107
|
-
const reserveTokens = finitePositive(policy?.historyCompact?.midTurn?.reserveTokens);
|
|
108
|
-
if (historyBudget === undefined || reserveTokens === undefined)
|
|
109
|
-
return undefined;
|
|
110
|
-
return { tokens: historyBudget + reserveTokens, source: 'policy_fallback' };
|
|
111
|
-
}
|
|
@@ -34,6 +34,56 @@ import { rewriteDurableToolResultProjectionArtifactRefs } from './durable-tool-r
|
|
|
34
34
|
import { buildModelProjectionTransition, decodeModelProjectionTransition, MODEL_PROJECTION_TRANSITION_EVENT_TYPE, } from '@maka/core/model-projection-transition';
|
|
35
35
|
import { baseToolResultProjection, decodeLedgerTransition, reduceEffectiveModelProjections, } from './model-projection-transition-ledger.js';
|
|
36
36
|
import { archivedToolResultProjection } from './tool-result-archive-transition.js';
|
|
37
|
+
/** Walks every typed StorageRef site reached by conversation-copy rewriting. */
|
|
38
|
+
function collectConversationCopyStorageRefs(input) {
|
|
39
|
+
const refs = [];
|
|
40
|
+
const addContent = (content) => {
|
|
41
|
+
if (content.kind === 'image')
|
|
42
|
+
refs.push(content.ref);
|
|
43
|
+
};
|
|
44
|
+
const addSerialized = (value) => {
|
|
45
|
+
if (isArchivedToolResultPlaceholder(value))
|
|
46
|
+
return;
|
|
47
|
+
try {
|
|
48
|
+
addContent(decodePersistedToolResultContent(markPersisted(value)));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Opaque tool results carry no typed StorageRef.
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
for (const message of input.messages) {
|
|
55
|
+
if (message.type === 'user' && message.attachments) {
|
|
56
|
+
for (const attachment of message.attachments)
|
|
57
|
+
refs.push(attachment.ref);
|
|
58
|
+
}
|
|
59
|
+
else if (message.type === 'tool_result') {
|
|
60
|
+
addContent(message.content);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const event of input.runtimeEvents) {
|
|
64
|
+
if (event.content?.kind === 'text' && event.content.attachments) {
|
|
65
|
+
for (const attachment of event.content.attachments)
|
|
66
|
+
refs.push(attachment.ref);
|
|
67
|
+
}
|
|
68
|
+
else if (event.content?.kind === 'function_response') {
|
|
69
|
+
addSerialized(event.content.result);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const serializedResult of input.archivedResults) {
|
|
73
|
+
addSerialized(deserializeToolResultArchive(serializedResult));
|
|
74
|
+
}
|
|
75
|
+
return refs;
|
|
76
|
+
}
|
|
77
|
+
/** Finds durable Session context references that the exact copy will rewrite. */
|
|
78
|
+
export function collectConversationCopySessionContextRefIds(input) {
|
|
79
|
+
const refIds = new Set();
|
|
80
|
+
for (const ref of collectConversationCopyStorageRefs(input)) {
|
|
81
|
+
if (ref.kind === 'session_context' && ref.sessionId === input.sourceSessionId) {
|
|
82
|
+
refIds.add(ref.refId);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return [...refIds].sort();
|
|
86
|
+
}
|
|
37
87
|
export function createConversationCopySlice(messages, sourceTurnId, boundary) {
|
|
38
88
|
const turnOrder = [];
|
|
39
89
|
const seen = new Set();
|
|
@@ -373,7 +423,8 @@ export function archivedToolResultContainsConversationOwnedReferences(serialized
|
|
|
373
423
|
if (content.kind === 'archived_tool_result')
|
|
374
424
|
return true;
|
|
375
425
|
if (content.kind === 'image') {
|
|
376
|
-
return content.ref.kind === 'session_file'
|
|
426
|
+
return ((content.ref.kind === 'session_file' || content.ref.kind === 'session_context') &&
|
|
427
|
+
content.ref.sessionId === sourceSessionId);
|
|
377
428
|
}
|
|
378
429
|
if (content.kind === 'subagent') {
|
|
379
430
|
const [linked] = conversationCopyLinkedChildReferences(content);
|
|
@@ -473,45 +524,10 @@ export function collectConversationCopyLinkedChildReferences(input) {
|
|
|
473
524
|
*/
|
|
474
525
|
export function collectConversationCopySessionFileRefs(input) {
|
|
475
526
|
const refs = new Set();
|
|
476
|
-
const
|
|
527
|
+
for (const ref of collectConversationCopyStorageRefs(input)) {
|
|
477
528
|
if (ref.kind === 'session_file' && ref.sessionId === input.sourceSessionId) {
|
|
478
529
|
refs.add(ref.relativePath);
|
|
479
530
|
}
|
|
480
|
-
};
|
|
481
|
-
const addContent = (content) => {
|
|
482
|
-
if (content.kind === 'image')
|
|
483
|
-
addRef(content.ref);
|
|
484
|
-
};
|
|
485
|
-
const addSerialized = (value) => {
|
|
486
|
-
if (isArchivedToolResultPlaceholder(value))
|
|
487
|
-
return;
|
|
488
|
-
try {
|
|
489
|
-
addContent(decodePersistedToolResultContent(markPersisted(value)));
|
|
490
|
-
}
|
|
491
|
-
catch {
|
|
492
|
-
// Opaque tool results carry no typed Session file reference.
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
for (const message of input.messages) {
|
|
496
|
-
if (message.type === 'user' && message.attachments) {
|
|
497
|
-
for (const attachment of message.attachments)
|
|
498
|
-
addRef(attachment.ref);
|
|
499
|
-
}
|
|
500
|
-
else if (message.type === 'tool_result') {
|
|
501
|
-
addContent(message.content);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
for (const event of input.runtimeEvents) {
|
|
505
|
-
if (event.content?.kind === 'text' && event.content.attachments) {
|
|
506
|
-
for (const attachment of event.content.attachments)
|
|
507
|
-
addRef(attachment.ref);
|
|
508
|
-
}
|
|
509
|
-
else if (event.content?.kind === 'function_response') {
|
|
510
|
-
addSerialized(event.content.result);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
for (const serializedResult of input.archivedResults) {
|
|
514
|
-
addSerialized(deserializeToolResultArchive(serializedResult));
|
|
515
531
|
}
|
|
516
532
|
return refs;
|
|
517
533
|
}
|
|
@@ -1218,15 +1234,22 @@ function linkedChildReferencesAreExternal(linked, externalChildReferences) {
|
|
|
1218
1234
|
linked.artifactIds.every((artifactId) => external.artifactIds.has(artifactId)));
|
|
1219
1235
|
}
|
|
1220
1236
|
function rewriteStorageRef(ref, references) {
|
|
1221
|
-
if (ref.kind
|
|
1222
|
-
|
|
1223
|
-
return ref;
|
|
1224
|
-
throw new Error('Conversation copy does not support Session context references yet');
|
|
1225
|
-
}
|
|
1226
|
-
if (ref.kind !== 'session_file' || ref.sessionId !== references.sourceSessionId)
|
|
1237
|
+
if ((ref.kind !== 'session_file' && ref.kind !== 'session_context') ||
|
|
1238
|
+
ref.sessionId !== references.sourceSessionId) {
|
|
1227
1239
|
return ref;
|
|
1240
|
+
}
|
|
1228
1241
|
if (references.mode === 'preserve_external')
|
|
1229
1242
|
return ref;
|
|
1243
|
+
if (ref.kind === 'session_context') {
|
|
1244
|
+
const refId = references.contextRefs?.get(ref.refId);
|
|
1245
|
+
if (!refId)
|
|
1246
|
+
throw new Error(`Conversation copy is missing Session context ${ref.refId}`);
|
|
1247
|
+
return {
|
|
1248
|
+
...ref,
|
|
1249
|
+
sessionId: references.targetSessionId,
|
|
1250
|
+
refId,
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1230
1253
|
const artifactId = references.artifactIds.get(ref.relativePath);
|
|
1231
1254
|
if (artifactId) {
|
|
1232
1255
|
return {
|
|
@@ -121,6 +121,37 @@ export function durableProjectionToToolResultOutput(projection) {
|
|
|
121
121
|
return { type: 'error-text', value: projection.message };
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
+
/** The images the artifact parts of one durable projection put in the request. */
|
|
125
|
+
export function projectionArtifactMedia(projection) {
|
|
126
|
+
if (projection.kind !== 'content')
|
|
127
|
+
return [];
|
|
128
|
+
return projection.parts.flatMap((part) => part.kind === 'artifact'
|
|
129
|
+
? [
|
|
130
|
+
{
|
|
131
|
+
label: part.ref.kind === 'session_context' ? part.ref.refId : part.ref.relativePath,
|
|
132
|
+
},
|
|
133
|
+
]
|
|
134
|
+
: []);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The images one Tool Result puts in the request: the artifact parts of its
|
|
138
|
+
* durable projection, or — for a pre-artifact image the decoder still hands to
|
|
139
|
+
* materialization raw — that legacy result.
|
|
140
|
+
*/
|
|
141
|
+
export function effectiveToolResultMedia(effective, sessionId) {
|
|
142
|
+
if (effective.kind === 'projection')
|
|
143
|
+
return projectionArtifactMedia(effective.projection);
|
|
144
|
+
if (effective.kind !== 'legacy_output')
|
|
145
|
+
return [];
|
|
146
|
+
const image = sessionImageResult(effective.output, sessionId);
|
|
147
|
+
if (!image)
|
|
148
|
+
return [];
|
|
149
|
+
return [
|
|
150
|
+
{
|
|
151
|
+
label: image.ref.kind === 'session_context' ? image.ref.refId : image.ref.relativePath,
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
}
|
|
124
155
|
/**
|
|
125
156
|
* The one pure decision of WHICH source a replayed Tool Result materializes
|
|
126
157
|
* from: a durable projection wins, and only a response that has none (legacy
|
|
@@ -16,9 +16,17 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Ways a summarizer's own output can be unusable. Owned here, in the
|
|
21
|
+
* history-compaction domain that produces and repairs them.
|
|
22
|
+
*/
|
|
23
|
+
const MALFORMED_HISTORY_COMPACT_SUMMARY_REASONS = [
|
|
24
|
+
'malformed_summary_missing_section',
|
|
25
|
+
'malformed_summary_truncated',
|
|
26
|
+
'malformed_summary_too_small_for_fold',
|
|
27
|
+
];
|
|
20
28
|
export function isMalformedHistoryCompactSummaryReason(reason) {
|
|
21
|
-
return
|
|
29
|
+
return MALFORMED_HISTORY_COMPACT_SUMMARY_REASONS.includes(reason);
|
|
22
30
|
}
|
|
23
31
|
export class HistoryCompactSummarizerError extends Error {
|
|
24
32
|
reason;
|
|
@@ -38,6 +38,12 @@ export function fitHistoryCompactMessages(messages, options) {
|
|
|
38
38
|
const message = bounded[messageIndex];
|
|
39
39
|
if (message.role !== 'tool')
|
|
40
40
|
continue;
|
|
41
|
+
// Accumulate replacements across the parts of this one tool message. A
|
|
42
|
+
// message that batches several tool results (one per parallel tool call)
|
|
43
|
+
// must keep every earlier placeholder, not just the last one — otherwise
|
|
44
|
+
// the returned history still carries full-size payloads even though the
|
|
45
|
+
// budget accounting already credited their removal.
|
|
46
|
+
let content = message.content;
|
|
41
47
|
for (let partIndex = 0; partIndex < message.content.length; partIndex += 1) {
|
|
42
48
|
const part = message.content[partIndex];
|
|
43
49
|
if (part.type !== 'tool-result')
|
|
@@ -53,7 +59,7 @@ export function fitHistoryCompactMessages(messages, options) {
|
|
|
53
59
|
const replacementChars = stableJsonLength(replacement);
|
|
54
60
|
if (replacementChars >= originalChars)
|
|
55
61
|
continue;
|
|
56
|
-
|
|
62
|
+
content = [...content];
|
|
57
63
|
content[partIndex] = replacement;
|
|
58
64
|
bounded = [...bounded];
|
|
59
65
|
bounded[messageIndex] = { ...message, content };
|
|
@@ -23,29 +23,24 @@ import { HistoryCompactSummarizerError, } from './history-compact-error.js';
|
|
|
23
23
|
import { findCheckpointSummaryDefect } from './history-compact-summary-validation.js';
|
|
24
24
|
import { buildHistoryCompactCheckpoint, historyCompactCheckpointToRuntimeEvent, matchHistoryCompactCheckpointPrefix, midTurnHeadAnchorEvent, projectHistoryCompactCheckpointReplay, } from './history-compact-checkpoint.js';
|
|
25
25
|
/**
|
|
26
|
-
* Estimate the token size of the next provider request
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* pure character guessing.
|
|
26
|
+
* Estimate the token size of the next provider request: the last request's real
|
|
27
|
+
* usage plus a signed char/4 payload delta for content the provider has not yet
|
|
28
|
+
* counted (or no longer carries). Without a usable usage sample the caller
|
|
29
|
+
* passes the whole payload as the delta against a zero baseline, so this stays
|
|
30
|
+
* one formula. This mirrors how surveyed peers avoid pure character guessing.
|
|
31
31
|
*/
|
|
32
32
|
export function estimateNextRequestTokens(input) {
|
|
33
33
|
const charsPerToken = Math.max(1, input.charsPerToken ?? 4);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return Math.max(0, estimateSignedChars(input.coldStartChars ?? input.appendedChars, charsPerToken));
|
|
34
|
+
const prior = input.priorUsageTokens !== undefined && Number.isFinite(input.priorUsageTokens)
|
|
35
|
+
? Math.max(0, Math.floor(input.priorUsageTokens))
|
|
36
|
+
: 0;
|
|
37
|
+
return Math.max(0, prior + estimateSignedChars(input.appendedChars, charsPerToken));
|
|
39
38
|
}
|
|
40
39
|
/** Proactive threshold: the next request would cross `contextWindow - reserve`. */
|
|
41
40
|
export function exceedsHighWater(estimatedTokens, contextWindow, reserveTokens) {
|
|
42
41
|
const highWater = Math.max(1, contextWindow - Math.max(0, reserveTokens));
|
|
43
42
|
return estimatedTokens > highWater;
|
|
44
43
|
}
|
|
45
|
-
/** Hard cap: the estimate exceeds the raw context window even before the reserve. */
|
|
46
|
-
export function exceedsContextWindow(estimatedTokens, contextWindow) {
|
|
47
|
-
return estimatedTokens > contextWindow;
|
|
48
|
-
}
|
|
49
44
|
/**
|
|
50
45
|
* Select the largest contiguous covered prefix that is safe to fold:
|
|
51
46
|
*
|
|
@@ -55,8 +50,8 @@ export function exceedsContextWindow(estimatedTokens, contextWindow) {
|
|
|
55
50
|
* - it leaves at least `reserveTailEvents` trailing events as the verbatim tail.
|
|
56
51
|
*
|
|
57
52
|
* Returns `no_safe_completed_span` when no such cut exists (e.g. the remaining
|
|
58
|
-
* pool is a single atomic call/result pair)
|
|
59
|
-
*
|
|
53
|
+
* pool is a single atomic call/result pair); the caller then fails open and
|
|
54
|
+
* sends the request unchanged.
|
|
60
55
|
*/
|
|
61
56
|
export function selectSafeCompactionPrefix(events, options = {}) {
|
|
62
57
|
const reserveTail = Math.max(0, Math.floor(options.reserveTailEvents ?? 0));
|
|
@@ -129,12 +124,8 @@ function estimateSignedChars(chars, charsPerToken) {
|
|
|
129
124
|
* Execute a triggered compaction command by deterministically folding the
|
|
130
125
|
* largest safe prefix. Trigger policy is owned by callers; once this function
|
|
131
126
|
* is called it always attempts the transaction. This plan is a pure shaper:
|
|
132
|
-
* when it cannot fold a safe
|
|
133
|
-
*
|
|
134
|
-
* never terminates the turn itself. The two failure tiers — fail open under
|
|
135
|
-
* the window, explicit `context_budget_exhausted` over it — are applied by the
|
|
136
|
-
* backend's final-request estimate owner, which re-measures the actual outgoing
|
|
137
|
-
* payload after all shaping (including this fold) has been applied.
|
|
127
|
+
* when it cannot fold a safe completed prefix it FAILS OPEN (keep the raw
|
|
128
|
+
* projection + diagnostic) and the request goes out unchanged.
|
|
138
129
|
*/
|
|
139
130
|
export async function planHistoryCompaction(input) {
|
|
140
131
|
const phase = input.phase ?? 'mid_turn';
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
*/
|
|
53
53
|
import { isPartialRuntimeEvent, isTerminalRuntimeEvent, runtimeEventHasModelVisibleContent, } from '@maka/core/runtime-event';
|
|
54
54
|
import { formatAttachmentResourceRef } from '@maka/core/attachments';
|
|
55
|
-
import { decodeEffectiveToolResultProjection, durableProjectionToToolResultOutput, } from './durable-tool-result-projection.js';
|
|
55
|
+
import { decodeEffectiveToolResultProjection, durableProjectionToToolResultOutput, effectiveToolResultMedia, } from './durable-tool-result-projection.js';
|
|
56
|
+
import { MATERIALIZED_IMAGE_TOKENS } from '@maka/core/attachments';
|
|
56
57
|
import { estimateTokens, stableJsonLength, turnKey } from './context-budget-helpers.js';
|
|
57
58
|
export const PROVIDER_REPLAY_PROJECTION_VERSION = 2;
|
|
58
59
|
/**
|
|
@@ -84,9 +85,9 @@ export function admitProviderReasoningReplayItems(items, providerReasoningReplay
|
|
|
84
85
|
// Effective model-history sizing
|
|
85
86
|
// ============================================================================
|
|
86
87
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
88
|
+
* Size of a `function_response`'s EFFECTIVE value — the durable projection
|
|
89
|
+
* when the response has one, never the raw execution fact the projection
|
|
90
|
+
* already bounded or redacted.
|
|
90
91
|
*
|
|
91
92
|
* Memoized on the content object because a legacy response (no durable
|
|
92
93
|
* projection) is re-derived through the whole compatibility codec, and one
|
|
@@ -94,7 +95,7 @@ export function admitProviderReasoningReplayItems(items, providerReasoningReplay
|
|
|
94
95
|
* compactable-content filter, and checkpoint prefix matching all walk the
|
|
95
96
|
* history. Content is immutable once committed, so identity is a sound key.
|
|
96
97
|
*/
|
|
97
|
-
const
|
|
98
|
+
const effectiveToolResultSizes = new WeakMap();
|
|
98
99
|
/** Model-visible character count of one materialized Tool Result output. */
|
|
99
100
|
function estimateToolResultOutputChars(output) {
|
|
100
101
|
switch (output.type) {
|
|
@@ -110,18 +111,24 @@ function estimateToolResultOutputChars(output) {
|
|
|
110
111
|
return output.reason?.length ?? 0;
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
|
-
|
|
114
|
-
const memoized =
|
|
114
|
+
function effectiveToolResultSize(content, sessionId) {
|
|
115
|
+
const memoized = effectiveToolResultSizes.get(content);
|
|
115
116
|
if (memoized !== undefined)
|
|
116
117
|
return memoized;
|
|
117
118
|
const effective = decodeEffectiveToolResultProjection(content, sessionId);
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
const size = {
|
|
120
|
+
chars: effective.kind === 'projection'
|
|
121
|
+
? estimateToolResultOutputChars(durableProjectionToToolResultOutput(effective.projection))
|
|
122
|
+
: effective.kind === 'invalid_legacy'
|
|
123
|
+
? effective.message.length
|
|
124
|
+
: stableJsonLength(effective.output),
|
|
125
|
+
mediaTokens: effectiveToolResultMedia(effective, sessionId).length * MATERIALIZED_IMAGE_TOKENS,
|
|
126
|
+
};
|
|
127
|
+
effectiveToolResultSizes.set(content, size);
|
|
128
|
+
return size;
|
|
129
|
+
}
|
|
130
|
+
export function estimateEffectiveToolResultChars(content, sessionId) {
|
|
131
|
+
return effectiveToolResultSize(content, sessionId).chars;
|
|
125
132
|
}
|
|
126
133
|
export function estimateRuntimeEventChars(event) {
|
|
127
134
|
let total = 0;
|
|
@@ -136,9 +143,27 @@ export function estimateRuntimeEventChars(event) {
|
|
|
136
143
|
total += content.message.length;
|
|
137
144
|
return total;
|
|
138
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Tokens one event costs beyond its characters. Stays in tokens instead of
|
|
148
|
+
* folding into the char count: `charsPerToken` calibrates text, and applying it
|
|
149
|
+
* to media would make an image cheaper on a session with a low text ratio.
|
|
150
|
+
*/
|
|
151
|
+
function estimateRuntimeEventMediaTokens(event) {
|
|
152
|
+
const content = event.content;
|
|
153
|
+
return content?.kind === 'function_response'
|
|
154
|
+
? effectiveToolResultSize(content, event.sessionId).mediaTokens
|
|
155
|
+
: 0;
|
|
156
|
+
}
|
|
139
157
|
export function estimateRuntimeEventsTokens(events, charsPerToken = 4) {
|
|
140
|
-
|
|
141
|
-
|
|
158
|
+
let chars = 0;
|
|
159
|
+
let mediaTokens = 0;
|
|
160
|
+
for (const event of events) {
|
|
161
|
+
if (event.modelVisibility === 'hidden')
|
|
162
|
+
continue;
|
|
163
|
+
chars += estimateRuntimeEventChars(event);
|
|
164
|
+
mediaTokens += estimateRuntimeEventMediaTokens(event);
|
|
165
|
+
}
|
|
166
|
+
return estimateTokens(chars, charsPerToken) + mediaTokens;
|
|
142
167
|
}
|
|
143
168
|
export function groupEventsByTurn(events, charsPerToken) {
|
|
144
169
|
const order = [];
|
|
@@ -16,50 +16,31 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
+
import { decodeEffectiveToolResultProjection, effectiveToolResultMedia, } from './durable-tool-result-projection.js';
|
|
19
20
|
function isRecord(value) {
|
|
20
21
|
return value !== null && typeof value === 'object';
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
value.relativePath.length > 0) {
|
|
28
|
-
return value.relativePath;
|
|
29
|
-
}
|
|
30
|
-
if (value.kind === 'session_context' &&
|
|
31
|
-
typeof value.refId === 'string' &&
|
|
32
|
-
value.refId.length > 0) {
|
|
33
|
-
return value.refId;
|
|
34
|
-
}
|
|
35
|
-
if (value.kind === 'external_file' &&
|
|
36
|
-
typeof value.absolutePath === 'string' &&
|
|
37
|
-
value.absolutePath.length > 0) {
|
|
38
|
-
return value.absolutePath;
|
|
39
|
-
}
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
23
|
+
/**
|
|
24
|
+
* The image Tool Results a rejected request can give back, read through the
|
|
25
|
+
* same decode the budget measures — not from the raw execution fact, which the
|
|
26
|
+
* durable projection may already have bounded or redacted.
|
|
27
|
+
*/
|
|
42
28
|
export function collectHistoricalImageToolResults(events) {
|
|
43
29
|
const collected = new Map();
|
|
44
30
|
for (const event of events) {
|
|
45
31
|
const content = event.content;
|
|
46
32
|
if (content?.kind !== 'function_response' || content.isError === true)
|
|
47
33
|
continue;
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
typeof result.mimeType !== 'string' ||
|
|
52
|
-
result.mimeType.length === 0) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
const artifactLabel = storageRefLabel(result.ref);
|
|
56
|
-
if (!artifactLabel)
|
|
34
|
+
const effective = decodeEffectiveToolResultProjection(content, event.sessionId);
|
|
35
|
+
const [media] = effectiveToolResultMedia(effective, event.sessionId);
|
|
36
|
+
if (!media || media.label.length === 0)
|
|
57
37
|
continue;
|
|
58
|
-
collected.set(content.id, { toolName: content.name, artifactLabel });
|
|
38
|
+
collected.set(content.id, { toolName: content.name, artifactLabel: media.label });
|
|
59
39
|
}
|
|
60
40
|
return collected;
|
|
61
41
|
}
|
|
62
|
-
|
|
42
|
+
/** A `file` part carrying inline image bytes, not a URL the provider fetches. */
|
|
43
|
+
export function isInlineImageFilePart(value) {
|
|
63
44
|
if (!isRecord(value) ||
|
|
64
45
|
value.type !== 'file' ||
|
|
65
46
|
typeof value.mediaType !== 'string' ||
|
|
@@ -446,6 +446,9 @@ function tokenUsageFromMessage(message) {
|
|
|
446
446
|
: {}),
|
|
447
447
|
...(message.promptSegments !== undefined ? { promptSegments: message.promptSegments } : {}),
|
|
448
448
|
...(message.contextBudget !== undefined ? { contextBudget: message.contextBudget } : {}),
|
|
449
|
+
...(message.lastRequestAnchor !== undefined
|
|
450
|
+
? { lastRequestAnchor: message.lastRequestAnchor }
|
|
451
|
+
: {}),
|
|
449
452
|
};
|
|
450
453
|
}
|
|
451
454
|
function messageTurnId(message) {
|
|
@@ -742,6 +742,9 @@ function projectTokenUsage(event, state, messages) {
|
|
|
742
742
|
: {}),
|
|
743
743
|
...(usage.promptSegments !== undefined ? { promptSegments: usage.promptSegments } : {}),
|
|
744
744
|
...(usage.contextBudget !== undefined ? { contextBudget: usage.contextBudget } : {}),
|
|
745
|
+
...(usage.lastRequestAnchor !== undefined
|
|
746
|
+
? { lastRequestAnchor: usage.lastRequestAnchor }
|
|
747
|
+
: {}),
|
|
745
748
|
...(event.refs?.providerRequestTraceId !== undefined
|
|
746
749
|
? { providerRequestTraceId: event.refs.providerRequestTraceId }
|
|
747
750
|
: {}),
|
|
@@ -857,13 +860,20 @@ function abortSourceFromRuntime(event, header) {
|
|
|
857
860
|
stringRecordValue(header, 'abortSource'));
|
|
858
861
|
}
|
|
859
862
|
function failureClassFromRuntimeEvent(event, header) {
|
|
860
|
-
|
|
863
|
+
const failureClass = stringStateDelta(event, 'failureClass') ??
|
|
861
864
|
stringStateDelta(event, 'errorClass') ??
|
|
862
865
|
stringStateDelta(event, 'reason') ??
|
|
863
866
|
stringStateDelta(event, 'code') ??
|
|
864
867
|
(event.content?.kind === 'error' ? nonEmptyString(event.content.reason) : undefined) ??
|
|
865
868
|
(event.content?.kind === 'error' ? nonEmptyString(event.content.code) : undefined) ??
|
|
866
|
-
header.failureClass
|
|
869
|
+
header.failureClass;
|
|
870
|
+
// Retired outcome. The runtime no longer decides locally that a request
|
|
871
|
+
// cannot be shaped to fit — the provider rejects it and recovery compacts and
|
|
872
|
+
// retries — so a turn that ends over the window is a context overflow like any
|
|
873
|
+
// other. Sessions written before that still carry the old name; fold it here,
|
|
874
|
+
// at the one place the durable ledger is read, so nothing downstream has to
|
|
875
|
+
// know two names for one outcome.
|
|
876
|
+
return failureClass === 'context_budget_exhausted' ? 'context_overflow' : failureClass;
|
|
867
877
|
}
|
|
868
878
|
function stringRecordValue(value, key) {
|
|
869
879
|
if (!value || typeof value !== 'object')
|
|
@@ -1107,6 +1117,7 @@ function semanticMessage(message) {
|
|
|
1107
1117
|
promptSegments: message.promptSegments,
|
|
1108
1118
|
contextBudget: message.contextBudget,
|
|
1109
1119
|
providerRequestTraceId: message.providerRequestTraceId,
|
|
1120
|
+
lastRequestAnchor: message.lastRequestAnchor,
|
|
1110
1121
|
};
|
|
1111
1122
|
case 'turn_state':
|
|
1112
1123
|
return {
|