maka-agent 0.2.0-dev.26.20260909 → 0.2.0-dev.28.20260911
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/LICENSE +106 -0
- package/dist/cli-core.js +28 -0
- package/dist/runtime-host-cli.js +43 -3
- package/dist/runtime-host-handoff-surface.js +10 -8
- package/dist/runtime-host-lifecycle-transaction.js +28 -3
- package/dist/runtime-host-local-source-retirement.js +1 -1
- package/dist/runtime-host-run-command.js +15 -7
- package/dist/runtime-host-setup-command.js +29 -6
- package/dist/runtime-host-update-command.js +49 -5
- package/dist/session-export-command.js +72 -0
- package/dist/session-import-command.js +69 -0
- 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/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +9 -4
- package/node_modules/@maka/core/dist/diagnostic-log.js +18 -7
- package/node_modules/@maka/core/dist/text-sanitize.js +8 -2
- package/node_modules/@maka/core/dist/usage-stats/types.js +2 -0
- package/node_modules/@maka/core/dist/workhub-routing.js +126 -0
- package/node_modules/@maka/core/package.json +2 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +15 -11
- package/node_modules/@maka/runtime/dist/archive-read-tool.js +3 -1
- package/node_modules/@maka/runtime/dist/bots/bot-test.js +2 -1
- package/node_modules/@maka/runtime/dist/bots/feishu-bridge.js +24 -20
- package/node_modules/@maka/runtime/dist/bots/slack-bridge.js +5 -3
- package/node_modules/@maka/runtime/dist/bots/wechat-bridge.js +87 -29
- package/node_modules/@maka/runtime/dist/bots/wecom-bridge.js +40 -36
- package/node_modules/@maka/runtime/dist/builtin-tools.js +21 -2
- package/node_modules/@maka/runtime/dist/computer-use-tools.js +4 -1
- package/node_modules/@maka/runtime/dist/deep-research-tools.js +11 -5
- package/node_modules/@maka/runtime/dist/edit-replace.js +2 -2
- package/node_modules/@maka/runtime/dist/filesystem-worker/operations.js +5 -6
- package/node_modules/@maka/runtime/dist/memory-extraction-evidence.js +7 -1
- package/node_modules/@maka/runtime/dist/memory-extraction.js +12 -7
- package/node_modules/@maka/runtime/dist/openai-responses-websocket.js +2 -5
- package/node_modules/@maka/runtime/dist/plugin-agent-service.js +131 -0
- package/node_modules/@maka/runtime/dist/plugin-approval-service.js +33 -0
- package/node_modules/@maka/runtime/dist/plugin-attachment-service.js +53 -0
- package/node_modules/@maka/runtime/dist/plugin-command-service.js +100 -0
- package/node_modules/@maka/runtime/dist/plugin-composition-loader.js +11 -2
- package/node_modules/@maka/runtime/dist/plugin-data-services.js +332 -0
- package/node_modules/@maka/runtime/dist/plugin-fs-service.js +62 -0
- package/node_modules/@maka/runtime/dist/plugin-goal-service.js +61 -0
- package/node_modules/@maka/runtime/dist/plugin-invocation-signal.js +24 -0
- package/node_modules/@maka/runtime/dist/plugin-llm-service.js +85 -0
- package/node_modules/@maka/runtime/dist/plugin-lsp-service.js +94 -0
- package/node_modules/@maka/runtime/dist/plugin-scope-registry.js +91 -0
- package/node_modules/@maka/runtime/dist/plugin-session-query-service.js +71 -0
- package/node_modules/@maka/runtime/dist/plugin-shell-env-service.js +87 -0
- package/node_modules/@maka/runtime/dist/plugin-shell-service.js +74 -0
- package/node_modules/@maka/runtime/dist/plugin-skill-service.js +93 -0
- package/node_modules/@maka/runtime/dist/plugin-system-prompt-service.js +346 -0
- package/node_modules/@maka/runtime/dist/plugin-tool-service.js +18 -52
- package/node_modules/@maka/runtime/dist/plugin-user-question-service.js +43 -0
- package/node_modules/@maka/runtime/dist/plugin-web-service.js +69 -0
- package/node_modules/@maka/runtime/dist/request-customization-fetch.js +14 -7
- package/node_modules/@maka/runtime/dist/session-export.js +178 -0
- package/node_modules/@maka/runtime/dist/session-import.js +100 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +6 -1
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +9 -6
- package/node_modules/@maka/runtime/dist/shell-run-tool-result.js +13 -6
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +43 -10
- package/node_modules/@maka/runtime/dist/stream-graph-handoff.js +6 -1
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +1 -4
- package/node_modules/@maka/runtime/dist/system-prompt/workspace-instructions.js +11 -6
- package/node_modules/@maka/runtime/dist/tavily-search.js +8 -2
- package/node_modules/@maka/runtime/dist/text-line-window.js +49 -0
- package/node_modules/@maka/runtime/dist/tool-output.js +35 -12
- package/node_modules/@maka/runtime/dist/unified-diff.js +40 -10
- package/node_modules/@maka/runtime/dist/web-fetch-tool.js +4 -2
- package/node_modules/@maka/runtime/dist/workers/filesystem-worker.js +82 -33
- package/node_modules/@maka/runtime/dist/workspace-executor.js +2 -6
- package/node_modules/@maka/runtime/package.json +20 -1
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +73 -24
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +46 -12
- package/node_modules/@maka/runtime-host/dist/operator/setup-frame.js +31 -21
- package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +7 -4
- package/node_modules/@maka/runtime-host/dist/protocol/artifact.js +2 -2
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +10 -1
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +30 -5
- package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +3 -2
- package/node_modules/@maka/runtime-host/dist/protocol/skill-catalog.js +2 -1
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +25 -23
- package/node_modules/@maka/runtime-host/dist/server/artifact-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +62 -4
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +481 -11
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +74 -0
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +6 -1
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +30 -0
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +114 -20
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +49 -12
- package/node_modules/@maka/runtime-host/dist/server/plugin-data-runtime.js +234 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +12 -0
- package/node_modules/@maka/runtime-host/dist/server/root-admission-owner.js +17 -8
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +189 -92
- package/node_modules/@maka/runtime-host/dist/server/runtime-resource-projection.js +3 -9
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +20 -21
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +24 -2
- package/node_modules/@maka/runtime-host/dist/server/skill-catalog-coordinator.js +18 -2
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -35
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +92 -10
- package/node_modules/@maka/runtime-host/dist/transport/framed-byte-stream-transport.js +4 -1
- package/node_modules/@maka/runtime-host/dist/transport/local-ipc-framing.js +4 -2
- package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +9 -8
- package/node_modules/@maka/runtime-host/dist/transport/resumable-peer-stream.js +14 -9
- package/node_modules/@maka/storage/dist/agent-run-store.js +54 -10
- package/node_modules/@maka/storage/dist/context-offload-snapshot.js +32 -10
- package/node_modules/@maka/storage/dist/context-value-mutation-gate.js +59 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +1 -1
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +909 -34
- package/node_modules/@maka/storage/dist/session-message-projection.js +8 -4
- package/node_modules/@maka/storage/dist/session-store.js +2 -2
- package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +18 -4
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +12 -5
- package/node_modules/@maka/storage/dist/stable-storage.js +2 -1
- package/node_modules/@maka/storage/package.json +2 -0
- package/node_modules/@modelcontextprotocol/client/dist/src-D_zzAWoS.mjs +22 -5
- package/node_modules/@modelcontextprotocol/client/dist/src-NAgB4Mp8.cjs +22 -5
- package/node_modules/zod/v4/core/memoizer.cjs +12 -16
- package/node_modules/zod/v4/core/memoizer.js +12 -16
- package/package.json +1 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Export one Session, and the subagent subtree under it, as a portable bundle.
|
|
21
|
+
*
|
|
22
|
+
* The preparation is `exportSessionBundleState()` in `@maka/storage`, which
|
|
23
|
+
* already takes a consistent database snapshot, keeps writers out, filters the
|
|
24
|
+
* copy down to the exported Sessions, carries the context-offload closure, and
|
|
25
|
+
* proves the result with `PRAGMA foreign_key_check`. This module is the thin
|
|
26
|
+
* part: point it at a workspace, ask for a quiescent Session, and seal what
|
|
27
|
+
* comes back with the bundle codec.
|
|
28
|
+
*
|
|
29
|
+
* What it adds on top is the manifest — the Sessions carried, the schema
|
|
30
|
+
* versions the SOURCE database registers, and the connection by slug and model
|
|
31
|
+
* so an importer can resolve it locally. No credential is carried: the state
|
|
32
|
+
* allow-list in the export policy decides what is copied, and configuration is
|
|
33
|
+
* not on it.
|
|
34
|
+
*/
|
|
35
|
+
import { mkdir, mkdtemp, rm, stat } from 'node:fs/promises';
|
|
36
|
+
import { join } from 'node:path';
|
|
37
|
+
import { tmpdir } from 'node:os';
|
|
38
|
+
import { SessionBundleFileError, } from '@maka/storage/session-bundle-contract';
|
|
39
|
+
import { createSessionBundleFileService } from '@maka/storage/session-bundle-file-service';
|
|
40
|
+
import { exportSessionBundleState, SessionBundleExportError, SESSION_BUNDLE_OMITTED_EVENT_TYPES, } from '@maka/storage/session-bundle-policy';
|
|
41
|
+
export const SESSION_EXPORT_MEDIA_TYPE = 'application/vnd.maka.session-export+json;version=1';
|
|
42
|
+
/** Generous by design; the codec is what enforces them. */
|
|
43
|
+
export const SESSION_EXPORT_BUNDLE_LIMITS = {
|
|
44
|
+
maxCompressedBytes: 2 * 1024 ** 3,
|
|
45
|
+
maxDecompressedTarBytes: 8 * 1024 ** 3,
|
|
46
|
+
maxPayloadBytes: 8 * 1024 ** 3,
|
|
47
|
+
maxFileBytes: 2 * 1024 ** 3,
|
|
48
|
+
maxEntryCount: 1_000_000,
|
|
49
|
+
maxManifestBytes: 4 * 1024 ** 2,
|
|
50
|
+
maxStateIdentityBytes: 4 * 1024 ** 2,
|
|
51
|
+
maxPathBytes: 4096,
|
|
52
|
+
maxPathDepth: 32,
|
|
53
|
+
};
|
|
54
|
+
export async function exportSessionBundle(input) {
|
|
55
|
+
if (!(await isDirectory(input.workspaceRoot))) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
reason: { kind: 'workspace_not_found', workspaceRoot: input.workspaceRoot },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (await exists(input.destination)) {
|
|
62
|
+
return { ok: false, reason: { kind: 'destination_exists' } };
|
|
63
|
+
}
|
|
64
|
+
const staging = await mkdtemp(join(tmpdir(), 'maka-session-export-'));
|
|
65
|
+
try {
|
|
66
|
+
const stateRoot = join(staging, 'state');
|
|
67
|
+
const workspaceRoot = join(staging, 'workspace');
|
|
68
|
+
// The codec seals a state tree beside a workspace tree. A Session's own
|
|
69
|
+
// files are the state; the user's project directory is not part of the
|
|
70
|
+
// conversation and is deliberately absent.
|
|
71
|
+
await mkdir(workspaceRoot, { recursive: true });
|
|
72
|
+
let plan;
|
|
73
|
+
try {
|
|
74
|
+
plan = await exportSessionBundleState({
|
|
75
|
+
stateRoot: input.workspaceRoot,
|
|
76
|
+
// Maka's desktop layout keeps state and configuration in one directory,
|
|
77
|
+
// so these are shared on purpose. Configuration stays out of the bundle
|
|
78
|
+
// because the policy copies an allow-list of state entries, not because
|
|
79
|
+
// the roots are apart.
|
|
80
|
+
configRoot: input.workspaceRoot,
|
|
81
|
+
allowShared: true,
|
|
82
|
+
destinationRoot: stateRoot,
|
|
83
|
+
sessionId: input.sessionId,
|
|
84
|
+
...(input.lease ? { lease: input.lease } : {}),
|
|
85
|
+
requireQuiescent: true,
|
|
86
|
+
includeSubtree: true,
|
|
87
|
+
omitDiagnostics: true,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const failure = asExportFailure(error, input.workspaceRoot);
|
|
92
|
+
if (failure)
|
|
93
|
+
return { ok: false, reason: failure };
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
const manifest = {
|
|
97
|
+
format: 'maka.session-export',
|
|
98
|
+
formatVersion: 1,
|
|
99
|
+
exportedAt: (input.now ?? Date.now)(),
|
|
100
|
+
rootSessionId: plan.sessionId,
|
|
101
|
+
sessionIds: plan.sessionIds,
|
|
102
|
+
schema: plan.sourceSchema,
|
|
103
|
+
connection: plan.connection,
|
|
104
|
+
includedEntries: plan.includedEntries,
|
|
105
|
+
excludedEntries: plan.excludedEntries,
|
|
106
|
+
diagnosticsOmitted: true,
|
|
107
|
+
omittedEventTypes: SESSION_BUNDLE_OMITTED_EVENT_TYPES,
|
|
108
|
+
};
|
|
109
|
+
const stateIdentity = {
|
|
110
|
+
mediaType: SESSION_EXPORT_MEDIA_TYPE,
|
|
111
|
+
bytes: Buffer.from(`${JSON.stringify(manifest)}\n`, 'utf8'),
|
|
112
|
+
};
|
|
113
|
+
const artifact = await createSessionBundleFileService().pack({
|
|
114
|
+
snapshot: { stateRoot, workspaceRoot, stateIdentity },
|
|
115
|
+
envelope: { sessionId: plan.sessionId },
|
|
116
|
+
destination: input.destination,
|
|
117
|
+
limits: input.limits ?? SESSION_EXPORT_BUNDLE_LIMITS,
|
|
118
|
+
});
|
|
119
|
+
return { ok: true, artifact, export: manifest };
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (error instanceof SessionBundleFileError) {
|
|
123
|
+
if (error.details?.quota !== undefined) {
|
|
124
|
+
return { ok: false, reason: { kind: 'bundle_limit', details: error.details } };
|
|
125
|
+
}
|
|
126
|
+
// The precheck fails fast, but the packer publishes atomically and can
|
|
127
|
+
// still lose the race. Reporting that as a generic IO failure would give
|
|
128
|
+
// a caller a different exit code for the same outcome.
|
|
129
|
+
if (error.code === 'destination_exists') {
|
|
130
|
+
return { ok: false, reason: { kind: 'destination_exists' } };
|
|
131
|
+
}
|
|
132
|
+
return { ok: false, reason: { kind: 'io_failed', message: error.message } };
|
|
133
|
+
}
|
|
134
|
+
const failure = asExportFailure(error, input.workspaceRoot);
|
|
135
|
+
if (failure)
|
|
136
|
+
return { ok: false, reason: failure };
|
|
137
|
+
return { ok: false, reason: { kind: 'io_failed', message: String(error) } };
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
await rm(staging, { recursive: true, force: true }).catch(() => { });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function asExportFailure(error, workspaceRoot) {
|
|
144
|
+
if (!(error instanceof SessionBundleExportError))
|
|
145
|
+
return undefined;
|
|
146
|
+
switch (error.code) {
|
|
147
|
+
case 'session_active':
|
|
148
|
+
return { kind: 'session_active', message: error.message };
|
|
149
|
+
case 'schema_unsupported':
|
|
150
|
+
return { kind: 'schema_unsupported', message: error.message };
|
|
151
|
+
case 'symlink':
|
|
152
|
+
case 'path_escape':
|
|
153
|
+
case 'unsupported_entry':
|
|
154
|
+
return { kind: 'artifact_unsafe', message: error.message };
|
|
155
|
+
case 'missing_entry':
|
|
156
|
+
return { kind: 'artifact_missing', message: error.message };
|
|
157
|
+
case 'invalid_root':
|
|
158
|
+
// The policy reports a missing Session through this code too, and only
|
|
159
|
+
// the message separates it from a directory that is not a workspace.
|
|
160
|
+
return /session does not exist/i.test(error.message)
|
|
161
|
+
? { kind: 'session_not_found' }
|
|
162
|
+
: { kind: 'workspace_not_found', workspaceRoot };
|
|
163
|
+
case 'destination_not_empty':
|
|
164
|
+
return { kind: 'destination_exists' };
|
|
165
|
+
default:
|
|
166
|
+
return { kind: 'io_failed', message: error.message };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function isDirectory(path) {
|
|
170
|
+
return stat(path)
|
|
171
|
+
.then((metadata) => metadata.isDirectory())
|
|
172
|
+
.catch(() => false);
|
|
173
|
+
}
|
|
174
|
+
async function exists(path) {
|
|
175
|
+
return stat(path)
|
|
176
|
+
.then(() => true)
|
|
177
|
+
.catch(() => false);
|
|
178
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Import a `.maka-session` bundle into a workspace.
|
|
21
|
+
*
|
|
22
|
+
* The mirror of the export: `hydrate()` verifies and unpacks what `pack()`
|
|
23
|
+
* sealed, and `importSessionBundleState()` merges what the export policy
|
|
24
|
+
* prepared. This module is the thin part -- verify, merge, report.
|
|
25
|
+
*
|
|
26
|
+
* The bundle names its connection by slug; nothing here resolves it. A Session
|
|
27
|
+
* whose slug this workspace does not have arrives in the state the app already
|
|
28
|
+
* shows for that: stale, and routable once the user picks a connection.
|
|
29
|
+
*/
|
|
30
|
+
import { mkdtemp, rm, stat } from 'node:fs/promises';
|
|
31
|
+
import { join } from 'node:path';
|
|
32
|
+
import { tmpdir } from 'node:os';
|
|
33
|
+
import { SessionBundleFileError, } from '@maka/storage/session-bundle-contract';
|
|
34
|
+
import { createSessionBundleFileService } from '@maka/storage/session-bundle-file-service';
|
|
35
|
+
import { importSessionBundleState, SessionBundleImportError, } from '@maka/storage/session-bundle-policy';
|
|
36
|
+
import { SESSION_EXPORT_BUNDLE_LIMITS } from './session-export.js';
|
|
37
|
+
export async function importSessionBundle(input) {
|
|
38
|
+
if (!(await isDirectory(input.workspaceRoot))) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
reason: { kind: 'workspace_not_found', workspaceRoot: input.workspaceRoot },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const limits = input.limits ?? SESSION_EXPORT_BUNDLE_LIMITS;
|
|
45
|
+
const staging = await mkdtemp(join(tmpdir(), 'maka-session-import-'));
|
|
46
|
+
try {
|
|
47
|
+
const service = createSessionBundleFileService();
|
|
48
|
+
// Read the identity before hydrating: the codec verifies a bundle against a
|
|
49
|
+
// Session id, and the bundle is the only thing that knows which one.
|
|
50
|
+
const inspection = await service.inspect({ source: { path: input.source }, limits });
|
|
51
|
+
const manifest = readExportManifest(inspection.stateIdentity.bytes);
|
|
52
|
+
const hydration = await service.hydrate({
|
|
53
|
+
source: { path: input.source },
|
|
54
|
+
limits,
|
|
55
|
+
expectedSessionId: manifest?.rootSessionId ?? inspection.manifest.envelope.sessionId,
|
|
56
|
+
destinationRoot: join(staging, 'hydrated'),
|
|
57
|
+
});
|
|
58
|
+
const merged = await importSessionBundleState({
|
|
59
|
+
stateRoot: input.workspaceRoot,
|
|
60
|
+
bundleStateRoot: hydration.stateRoot,
|
|
61
|
+
...(input.lease ? { lease: input.lease } : {}),
|
|
62
|
+
});
|
|
63
|
+
return { ok: true, ...merged, manifest };
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error instanceof SessionBundleImportError) {
|
|
67
|
+
switch (error.code) {
|
|
68
|
+
case 'session_exists':
|
|
69
|
+
return { ok: false, reason: { kind: 'session_exists', message: error.message } };
|
|
70
|
+
case 'schema_unsupported':
|
|
71
|
+
return { ok: false, reason: { kind: 'schema_unsupported', message: error.message } };
|
|
72
|
+
case 'conflict':
|
|
73
|
+
return { ok: false, reason: { kind: 'conflict', message: error.message } };
|
|
74
|
+
default:
|
|
75
|
+
return { ok: false, reason: { kind: 'io_failed', message: error.message } };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (error instanceof SessionBundleFileError) {
|
|
79
|
+
return { ok: false, reason: { kind: 'bundle_unreadable', message: error.message } };
|
|
80
|
+
}
|
|
81
|
+
return { ok: false, reason: { kind: 'io_failed', message: String(error) } };
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
await rm(staging, { recursive: true, force: true }).catch(() => { });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function readExportManifest(bytes) {
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(Buffer.from(bytes).toString('utf8'));
|
|
90
|
+
return parsed.format === 'maka.session-export' ? parsed : undefined;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function isDirectory(path) {
|
|
97
|
+
return stat(path)
|
|
98
|
+
.then((metadata) => metadata.isDirectory())
|
|
99
|
+
.catch(() => false);
|
|
100
|
+
}
|
|
@@ -3608,7 +3608,12 @@ function buildAgentOutputCommittedResult(input) {
|
|
|
3608
3608
|
truncated: withoutText.omittedArtifactIds > 0,
|
|
3609
3609
|
};
|
|
3610
3610
|
}
|
|
3611
|
-
const codePoints =
|
|
3611
|
+
const codePoints = [];
|
|
3612
|
+
for (const point of text) {
|
|
3613
|
+
if (codePoints.length >= input.maxBytes)
|
|
3614
|
+
break;
|
|
3615
|
+
codePoints.push(point);
|
|
3616
|
+
}
|
|
3612
3617
|
let low = 0;
|
|
3613
3618
|
let high = codePoints.length;
|
|
3614
3619
|
let best = { ...withoutText, textTruncated: true };
|
|
@@ -1677,13 +1677,16 @@ function normalizeBackgroundTimeoutMs(value) {
|
|
|
1677
1677
|
}
|
|
1678
1678
|
return value;
|
|
1679
1679
|
}
|
|
1680
|
-
function splitPtyData(data) {
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1680
|
+
function* splitPtyData(data) {
|
|
1681
|
+
// Bound each published piece without materializing the entire callback as code points.
|
|
1682
|
+
let offset = 0;
|
|
1683
|
+
while (offset < data.length) {
|
|
1684
|
+
const start = offset;
|
|
1685
|
+
for (let count = 0; count < PTY_RAW_INPUT_CHUNK_CODE_POINTS && offset < data.length; count++) {
|
|
1686
|
+
offset += data.codePointAt(offset) > 0xffff ? 2 : 1;
|
|
1687
|
+
}
|
|
1688
|
+
yield data.slice(start, offset);
|
|
1685
1689
|
}
|
|
1686
|
-
return chunks;
|
|
1687
1690
|
}
|
|
1688
1691
|
function encodedPtyDataBytes(data) {
|
|
1689
1692
|
return Buffer.byteLength(JSON.stringify(data), 'utf8');
|
|
@@ -212,16 +212,23 @@ function takeTailText(text, budget) {
|
|
|
212
212
|
return { text: `${TRUNCATED_MARKER}\n${tail}`, truncated: true };
|
|
213
213
|
}
|
|
214
214
|
function sliceUtf8Tail(text, budget) {
|
|
215
|
-
const characters =
|
|
216
|
-
let result = '';
|
|
215
|
+
const characters = [];
|
|
217
216
|
let bytes = 0;
|
|
218
|
-
for (let
|
|
219
|
-
|
|
217
|
+
for (let end = text.length; end > 0;) {
|
|
218
|
+
let start = end - 1;
|
|
219
|
+
const last = text.charCodeAt(start);
|
|
220
|
+
if (last >= 0xdc00 && last <= 0xdfff && start > 0) {
|
|
221
|
+
const previous = text.charCodeAt(start - 1);
|
|
222
|
+
if (previous >= 0xd800 && previous <= 0xdbff)
|
|
223
|
+
start -= 1;
|
|
224
|
+
}
|
|
225
|
+
const character = text.slice(start, end);
|
|
220
226
|
const size = Buffer.byteLength(character, 'utf8');
|
|
221
227
|
if (bytes + size > budget)
|
|
222
228
|
break;
|
|
223
|
-
|
|
229
|
+
characters.push(character);
|
|
224
230
|
bytes += size;
|
|
231
|
+
end = start;
|
|
225
232
|
}
|
|
226
|
-
return
|
|
233
|
+
return characters.reverse().join('');
|
|
227
234
|
}
|
|
@@ -286,17 +286,30 @@ export class AgentGraphCoordinator {
|
|
|
286
286
|
/** Reconcile now and surface any host-level failure to explicit callers. */
|
|
287
287
|
async reconcile(rootSessionId) {
|
|
288
288
|
await this.#assertRootSupervisor(rootSessionId);
|
|
289
|
-
|
|
290
|
-
driver.
|
|
291
|
-
driver.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
289
|
+
let driver = await this.#driver(rootSessionId);
|
|
290
|
+
// A lookup started before handover can return after its driver retired.
|
|
291
|
+
while (driver.closed && !this.#closed)
|
|
292
|
+
driver = await this.#driver(rootSessionId);
|
|
293
|
+
driver.reconciliationReaders += 1;
|
|
294
|
+
try {
|
|
295
|
+
driver.lastError = undefined;
|
|
296
|
+
driver.paused = false;
|
|
297
|
+
this.#requestDrive(driver);
|
|
298
|
+
// An epoch handover must not redirect this caller to the next driver.
|
|
299
|
+
while (driver.task)
|
|
300
|
+
await driver.task;
|
|
301
|
+
if (driver.lastError !== undefined)
|
|
302
|
+
throw driver.lastError;
|
|
303
|
+
if (!driver.lastResult) {
|
|
304
|
+
throw new Error(`Agent graph ${driver.graphId} produced no reconciliation result`);
|
|
305
|
+
}
|
|
306
|
+
return driver.lastResult;
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
driver.reconciliationReaders -= 1;
|
|
310
|
+
if (driver.closed && driver.reconciliationReaders === 0)
|
|
311
|
+
driver.lastResult = undefined;
|
|
298
312
|
}
|
|
299
|
-
return driver.lastResult;
|
|
300
313
|
}
|
|
301
314
|
async waitForIdle(rootSessionId) {
|
|
302
315
|
const driver = await this.#driver(rootSessionId);
|
|
@@ -1014,15 +1027,32 @@ export class AgentGraphCoordinator {
|
|
|
1014
1027
|
const latest = await this.currentGraphEpoch(rootSessionId);
|
|
1015
1028
|
if (latest.graphId !== current.graphId) {
|
|
1016
1029
|
selected = latest;
|
|
1030
|
+
if (driver)
|
|
1031
|
+
void this.#retireDriver(driver);
|
|
1017
1032
|
return;
|
|
1018
1033
|
}
|
|
1019
1034
|
if ((await this.#readSessionStateForGraph(rootSessionId, current.graphId)) !== 'terminal') {
|
|
1020
1035
|
return;
|
|
1021
1036
|
}
|
|
1022
1037
|
selected = await this.advanceGraphEpoch(rootSessionId, current);
|
|
1038
|
+
if (driver)
|
|
1039
|
+
void this.#retireDriver(driver);
|
|
1023
1040
|
});
|
|
1024
1041
|
return selected;
|
|
1025
1042
|
}
|
|
1043
|
+
async #retireDriver(driver) {
|
|
1044
|
+
// Tool closures can outlive their epoch. Fence them and let already
|
|
1045
|
+
// admitted operations finish before releasing their complete snapshots.
|
|
1046
|
+
// Cleanup belongs to the old epoch; its teardown I/O must not block the next.
|
|
1047
|
+
driver.closed = true;
|
|
1048
|
+
driver.requested = false;
|
|
1049
|
+
await Promise.allSettled([driver.task, driver.stopTask]);
|
|
1050
|
+
await this.#waitForClientProjectionUpdates(driver);
|
|
1051
|
+
if (driver.reconciliationReaders === 0)
|
|
1052
|
+
driver.lastResult = undefined;
|
|
1053
|
+
driver.runtimeFailureRunIds.clear();
|
|
1054
|
+
// Keep the lightweight driver for projection repair and close diagnostics.
|
|
1055
|
+
}
|
|
1026
1056
|
async #readSessionStateForGraph(rootSessionId, graphId) {
|
|
1027
1057
|
const snapshot = buildAgentGraphClientSnapshot(await this.#readClientModelInputForGraph(rootSessionId, graphId));
|
|
1028
1058
|
if (snapshot.scheduleRevision === 0)
|
|
@@ -1094,6 +1124,7 @@ export class AgentGraphCoordinator {
|
|
|
1094
1124
|
stopGeneration: 0,
|
|
1095
1125
|
driveGeneration: 0,
|
|
1096
1126
|
closed: false,
|
|
1127
|
+
reconciliationReaders: 0,
|
|
1097
1128
|
clientProjectionDirty: false,
|
|
1098
1129
|
runtimeFailureRunIds: new Set(),
|
|
1099
1130
|
yieldWaiters: new Set(),
|
|
@@ -1125,6 +1156,8 @@ export class AgentGraphCoordinator {
|
|
|
1125
1156
|
this.#requestDrive(driver);
|
|
1126
1157
|
}
|
|
1127
1158
|
#requestDrive(driver) {
|
|
1159
|
+
if (driver.closed || this.#closed)
|
|
1160
|
+
return;
|
|
1128
1161
|
driver.requested = true;
|
|
1129
1162
|
if (driver.task)
|
|
1130
1163
|
return;
|
|
@@ -143,7 +143,12 @@ function truncateUtf8(text, maxBytes) {
|
|
|
143
143
|
const ellipsisBytes = Buffer.byteLength(ellipsis, 'utf8');
|
|
144
144
|
if (maxBytes < ellipsisBytes)
|
|
145
145
|
return '';
|
|
146
|
-
const codePoints =
|
|
146
|
+
const codePoints = [];
|
|
147
|
+
for (const point of text) {
|
|
148
|
+
if (codePoints.length >= maxBytes)
|
|
149
|
+
break;
|
|
150
|
+
codePoints.push(point);
|
|
151
|
+
}
|
|
147
152
|
let low = 0;
|
|
148
153
|
let high = codePoints.length;
|
|
149
154
|
let best = ellipsis;
|
|
@@ -151,10 +151,7 @@ async function checkedOpenAiCodexFetch(fetchFn, url, init, refreshOAuthAccessTok
|
|
|
151
151
|
const response = await fetchFn(url, requestInit);
|
|
152
152
|
if (response.ok)
|
|
153
153
|
return response;
|
|
154
|
-
const detail = await response
|
|
155
|
-
.clone()
|
|
156
|
-
.text()
|
|
157
|
-
.catch(() => '');
|
|
154
|
+
const detail = await response.text().catch(() => '');
|
|
158
155
|
if (response.status === 401 &&
|
|
159
156
|
!authorizationReplayUsed &&
|
|
160
157
|
refreshOAuthAccessToken &&
|
|
@@ -112,12 +112,12 @@ async function readWorkspaceInstructions(rootCandidate, scope) {
|
|
|
112
112
|
if (seenDigests.has(digest))
|
|
113
113
|
continue;
|
|
114
114
|
seenDigests.add(digest);
|
|
115
|
-
const
|
|
115
|
+
const text = truncateCodepoints(cleaned, MAX_WORKSPACE_INSTRUCTION_FILE_CHARS);
|
|
116
116
|
out.push({
|
|
117
117
|
file,
|
|
118
118
|
scope,
|
|
119
|
-
text
|
|
120
|
-
truncated:
|
|
119
|
+
text,
|
|
120
|
+
truncated: text.length < cleaned.length,
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
catch {
|
|
@@ -130,8 +130,13 @@ function cleanPromptText(text) {
|
|
|
130
130
|
return text.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '');
|
|
131
131
|
}
|
|
132
132
|
function truncateCodepoints(text, max) {
|
|
133
|
-
|
|
134
|
-
if (chars.length <= max)
|
|
133
|
+
if (text.length <= max)
|
|
135
134
|
return text;
|
|
136
|
-
|
|
135
|
+
const chars = [];
|
|
136
|
+
for (const char of text) {
|
|
137
|
+
if (chars.length >= max)
|
|
138
|
+
return chars.join('');
|
|
139
|
+
chars.push(char);
|
|
140
|
+
}
|
|
141
|
+
return text;
|
|
137
142
|
}
|
|
@@ -126,9 +126,9 @@ function mapTavilyRows(raw, limit) {
|
|
|
126
126
|
continue;
|
|
127
127
|
rows.push({
|
|
128
128
|
provider: 'tavily',
|
|
129
|
-
title: safeString(rawRow.title, location.url)
|
|
129
|
+
title: truncateResultText(safeString(rawRow.title, location.url), TAVILY_RESULT_TITLE_MAX_CHARS),
|
|
130
130
|
url: location.url,
|
|
131
|
-
snippet: safeString(rawRow.content)
|
|
131
|
+
snippet: truncateResultText(safeString(rawRow.content), TAVILY_RESULT_SNIPPET_MAX_CHARS),
|
|
132
132
|
source: location.source,
|
|
133
133
|
});
|
|
134
134
|
if (rows.length >= limit)
|
|
@@ -166,6 +166,12 @@ async function readBoundedJson(response, maxBytes) {
|
|
|
166
166
|
function safeString(value, fallback = '') {
|
|
167
167
|
return typeof value === 'string' ? value : fallback;
|
|
168
168
|
}
|
|
169
|
+
function truncateResultText(value, maxChars) {
|
|
170
|
+
if (value.length <= maxChars)
|
|
171
|
+
return value;
|
|
172
|
+
// Detach bounded results from large provider strings, preserving even unpaired UTF-16 surrogates.
|
|
173
|
+
return Buffer.from(value.slice(0, maxChars), 'utf16le').toString('utf16le');
|
|
174
|
+
}
|
|
169
175
|
function normalizeResultLocation(value) {
|
|
170
176
|
if (typeof value !== 'string' || value.length > TAVILY_RESULT_URL_MAX_CHARS)
|
|
171
177
|
return null;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
// Match split('\n').slice(offset, end).join('\n'), without creating entries
|
|
20
|
+
// for lines outside the requested window. Empty and trailing lines count too.
|
|
21
|
+
// A missing or zero limit is unbounded: end is the total line count.
|
|
22
|
+
export function readTextLineWindow(content, offset, limit) {
|
|
23
|
+
if (offset === undefined && limit === undefined)
|
|
24
|
+
return content;
|
|
25
|
+
let lineCount = 1;
|
|
26
|
+
for (let cursor = content.indexOf('\n'); cursor !== -1; cursor = content.indexOf('\n', cursor + 1)) {
|
|
27
|
+
lineCount++;
|
|
28
|
+
}
|
|
29
|
+
const start = offset ?? 0;
|
|
30
|
+
const end = limit ? start + limit : lineCount;
|
|
31
|
+
const from = sliceIndex(start, lineCount);
|
|
32
|
+
const to = sliceIndex(end, lineCount);
|
|
33
|
+
if (from >= to)
|
|
34
|
+
return '';
|
|
35
|
+
const selected = [];
|
|
36
|
+
let cursor = 0;
|
|
37
|
+
for (let line = 0; line < to; line++) {
|
|
38
|
+
const newline = content.indexOf('\n', cursor);
|
|
39
|
+
const lineEnd = newline === -1 ? content.length : newline;
|
|
40
|
+
if (line >= from)
|
|
41
|
+
selected.push(content.slice(cursor, lineEnd));
|
|
42
|
+
cursor = lineEnd + 1;
|
|
43
|
+
}
|
|
44
|
+
return selected.join('\n');
|
|
45
|
+
}
|
|
46
|
+
function sliceIndex(value, length) {
|
|
47
|
+
const integer = Math.trunc(value) || 0;
|
|
48
|
+
return integer < 0 ? Math.max(length + integer, 0) : Math.min(integer, length);
|
|
49
|
+
}
|