maka-agent 0.2.0-dev.24.20260907 → 0.2.0-dev.26.20260909
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -19
- package/README.zh-CN.md +34 -19
- package/dist/mcp-capability-provider.js +4 -1
- package/dist/pi-transcript-tools.js +6 -0
- package/dist/pi-tui-layout.js +56 -10
- package/dist/pi-tui-mcp-status.js +2 -2
- package/dist/pi-tui-pickers.js +121 -30
- package/dist/pi-tui-runner.js +156 -42
- package/dist/pi-tui-todo.js +221 -0
- package/dist/pi-tui-transcript-viewer.js +134 -7
- package/dist/runtime-host-cli-context.js +70 -13
- package/dist/runtime-host-handoff-surface.js +51 -26
- package/dist/runtime-host-local-handoff.js +11 -1
- package/dist/runtime-host-service-manager.js +0 -3
- package/dist/runtime-host-session-channel.js +13 -0
- package/dist/runtime-host-session-driver.js +42 -1
- package/dist/tui-autocomplete-layout.js +3 -1
- package/dist/tui-copy-catalog.js +85 -6
- package/dist/tui-editor-render.js +36 -0
- package/dist/tui-mcp-control.js +41 -6
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/agent-run.js +1 -0
- package/node_modules/@maka/core/dist/attachments.js +3 -0
- package/node_modules/@maka/core/dist/canonical-json.js +27 -0
- package/node_modules/@maka/core/dist/capabilities.js +35 -0
- package/node_modules/@maka/core/dist/computer-use.js +0 -1
- package/node_modules/@maka/core/dist/display-redaction.js +18 -0
- package/node_modules/@maka/core/dist/llm-connections.js +13 -0
- package/node_modules/@maka/core/dist/model-call-attempt.js +2 -0
- package/node_modules/@maka/core/dist/model-metadata.js +4 -0
- package/node_modules/@maka/core/dist/redaction.js +35 -0
- package/node_modules/@maka/core/dist/run-composition.js +77 -2
- package/node_modules/@maka/core/dist/runtime-event.js +3 -0
- package/node_modules/@maka/core/dist/session.js +57 -6
- package/node_modules/@maka/core/dist/slash-command-catalog.js +1 -0
- package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
- package/node_modules/@maka/core/dist/workhub-action-result.js +68 -0
- package/node_modules/@maka/core/package.json +3 -3
- package/node_modules/@maka/eval/dist/experiment-directory.js +4 -13
- package/node_modules/@maka/eval/dist/toolchain-verification.js +1 -1
- package/node_modules/@maka/mcp/dist/form-elicitation.js +213 -0
- package/node_modules/@maka/mcp/dist/index.js +337 -71
- package/node_modules/@maka/runtime/dist/agent-run-inspect.js +0 -13
- package/node_modules/@maka/runtime/dist/agent-run.js +90 -2
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +16 -8
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +57 -50
- package/node_modules/@maka/runtime/dist/archive-read-tool.js +2 -2
- package/node_modules/@maka/runtime/dist/context-budget-helpers.js +0 -16
- package/node_modules/@maka/runtime/dist/conversation-copy.js +153 -11
- package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +3 -3
- package/node_modules/@maka/runtime/dist/goal-continuation.js +5 -1
- package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +69 -1
- package/node_modules/@maka/runtime/dist/mcp-tools.js +7 -2
- package/node_modules/@maka/runtime/dist/model-fetcher.js +42 -2
- package/node_modules/@maka/runtime/dist/model-history.js +0 -112
- package/node_modules/@maka/runtime/dist/plugin-runtime.js +20 -10
- package/node_modules/@maka/runtime/dist/plugin-tool-service.js +205 -0
- package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +8 -1
- package/node_modules/@maka/runtime/dist/request-shape.js +23 -0
- package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +1 -1
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +24 -1
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +127 -13
- package/node_modules/@maka/runtime/dist/session-manager.js +20 -36
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +21 -0
- package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +0 -5
- package/node_modules/@maka/runtime/dist/test-connection.js +0 -1
- package/node_modules/@maka/runtime/dist/tool-activation-identity.js +41 -0
- package/node_modules/@maka/runtime/dist/tool-availability.js +11 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +63 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +42 -12
- package/node_modules/@maka/runtime/dist/tool-result-archive.js +27 -4
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/client/connection.js +30 -5
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +52 -13
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +4 -2
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +18 -1
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +19 -0
- package/node_modules/@maka/runtime-host/dist/control/endpoint.js +1 -3
- package/node_modules/@maka/runtime-host/dist/node-error.js +22 -0
- package/node_modules/@maka/runtime-host/dist/operator/managed-deployment.js +1 -3
- package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +0 -1
- package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +1 -1
- package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +1 -3
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +9 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +3 -2
- package/node_modules/@maka/runtime-host/dist/protocol/plan.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +27 -5
- package/node_modules/@maka/runtime-host/dist/protocol/project-catalog.js +2 -2
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +86 -157
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +0 -3
- package/node_modules/@maka/runtime-host/dist/server/canonical-session-projection.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +28 -8
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +112 -56
- package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +32 -32
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +85 -25
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +42 -14
- package/node_modules/@maka/runtime-host/dist/server/failure-diagnostic.js +24 -0
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +7 -3
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -3
- package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +3 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +34 -4
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +51 -23
- package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +2 -7
- 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 +6 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +110 -14
- package/node_modules/@maka/runtime-host/dist/server/runtime-resource-coordinator.js +11 -8
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +31 -21
- package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +20 -8
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +15 -104
- package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +3 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +59 -82
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +132 -139
- package/node_modules/@maka/runtime-host/dist/server/workhub-message-attachments.js +46 -0
- package/node_modules/@maka/storage/dist/agent-run-store.js +14 -1
- package/node_modules/@maka/storage/dist/artifact-store.js +40 -3
- package/node_modules/@maka/storage/dist/atomic-file-write.js +74 -0
- package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -1
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +284 -196
- package/node_modules/@maka/storage/dist/credential-store.js +12 -63
- package/node_modules/@maka/storage/dist/execution-stores.js +4 -1
- package/node_modules/@maka/storage/dist/git-exec.js +61 -0
- package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +8 -35
- package/node_modules/@maka/storage/dist/goal-authority.js +2 -2
- package/node_modules/@maka/storage/dist/interaction-store.js +2 -2
- package/node_modules/@maka/storage/dist/mcp-config-store.js +10 -25
- package/node_modules/@maka/storage/dist/project-catalog.js +2 -23
- package/node_modules/@maka/storage/dist/runtime-event-invariants.js +2 -2
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +8 -3
- package/node_modules/@maka/storage/dist/scheduled-task-store.js +249 -283
- package/node_modules/@maka/storage/dist/session-bundle-manifest.js +5 -17
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +2 -1
- package/node_modules/@maka/storage/dist/session-copy-cleanup.js +2 -1
- package/node_modules/@maka/storage/dist/session-store.js +14 -2
- package/node_modules/@maka/storage/dist/settings-store.js +8 -4
- package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +11 -0
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +1 -2
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +12 -1
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +67 -3
- package/node_modules/@maka/storage/dist/stable-storage.js +14 -1
- package/node_modules/@maka/storage/dist/storage-id.js +28 -0
- package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +31 -4
- package/node_modules/@maka/storage/dist/workspace-identity.js +2 -15
- package/node_modules/@maka/storage/dist/workspace-version-authority-internal.js +1 -8
- package/package.json +1 -1
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +0 -990
- package/node_modules/@maka/core/dist/workhub-session-resolver.js +0 -50
|
@@ -22,8 +22,12 @@ import { basename, join, resolve, sep } from 'node:path';
|
|
|
22
22
|
import { isSupportedCodexThreadSource, sanitizeForeignTitle } from '@maka/core/foreign-session';
|
|
23
23
|
import { externalSessionMatchesQuery } from '@maka/core/external-session';
|
|
24
24
|
export const CODEX_SESSION_ADAPTER_ID = 'codex';
|
|
25
|
-
export const CODEX_ROLLOUT_MAX_BYTES =
|
|
25
|
+
export const CODEX_ROLLOUT_MAX_BYTES = 2 * 1024 * 1024 * 1024;
|
|
26
26
|
const CODEX_ROLLOUT_HEAD_BYTES = 512 * 1024;
|
|
27
|
+
const CODEX_ROLLOUT_READ_BYTES = 64 * 1024;
|
|
28
|
+
const CODEX_ROLLOUT_MAX_RECORD_BYTES = 64 * 1024 * 1024;
|
|
29
|
+
const CODEX_ROLLOUT_MAX_CONVERTED_BYTES = 256 * 1024 * 1024;
|
|
30
|
+
const CODEX_ROLLOUT_MAX_MESSAGES = 250_000;
|
|
27
31
|
const CODEX_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
28
32
|
const CODEX_UNSAFE_PATH_CHARS = /[\u0000-\u001F\u007F\u0080-\u009F\u061C\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]/;
|
|
29
33
|
/**
|
|
@@ -40,12 +44,19 @@ export class CodexSessionAdapter {
|
|
|
40
44
|
id = CODEX_SESSION_ADAPTER_ID;
|
|
41
45
|
codexHome;
|
|
42
46
|
maxRolloutBytes;
|
|
47
|
+
maxRecordBytes;
|
|
48
|
+
maxConvertedBytes;
|
|
49
|
+
maxMessages;
|
|
43
50
|
constructor(options = {}) {
|
|
44
51
|
this.codexHome = resolve(options.codexHome ?? process.env.CODEX_HOME ?? join(homedir(), '.codex'));
|
|
45
52
|
this.maxRolloutBytes = options.maxRolloutBytes ?? CODEX_ROLLOUT_MAX_BYTES;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
this.maxRecordBytes = options.maxRecordBytes ?? CODEX_ROLLOUT_MAX_RECORD_BYTES;
|
|
54
|
+
this.maxConvertedBytes = options.maxConvertedBytes ?? CODEX_ROLLOUT_MAX_CONVERTED_BYTES;
|
|
55
|
+
this.maxMessages = options.maxMessages ?? CODEX_ROLLOUT_MAX_MESSAGES;
|
|
56
|
+
assertPositiveSafeInteger(this.maxRolloutBytes, 'Codex rollout byte limit');
|
|
57
|
+
assertPositiveSafeInteger(this.maxRecordBytes, 'Codex rollout record byte limit');
|
|
58
|
+
assertPositiveSafeInteger(this.maxConvertedBytes, 'Codex converted message byte limit');
|
|
59
|
+
assertPositiveSafeInteger(this.maxMessages, 'Codex converted message count limit');
|
|
49
60
|
}
|
|
50
61
|
async detect() {
|
|
51
62
|
return ((await isDirectory(join(this.codexHome, 'sessions'))) ||
|
|
@@ -64,13 +75,12 @@ export class CodexSessionAdapter {
|
|
|
64
75
|
const rolloutPath = await this.resolveRolloutPath(catalogEntry.rolloutPath, sessionId);
|
|
65
76
|
if (!rolloutPath)
|
|
66
77
|
throw new Error(`Codex rollout is unavailable: ${sessionId}`);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
78
|
+
return convertCodexRollout(rolloutPath, sessionId, catalogEntry.name, catalogEntry.cwd, {
|
|
79
|
+
maxRolloutBytes: this.maxRolloutBytes,
|
|
80
|
+
maxRecordBytes: this.maxRecordBytes,
|
|
81
|
+
maxConvertedBytes: this.maxConvertedBytes,
|
|
82
|
+
maxMessages: this.maxMessages,
|
|
83
|
+
});
|
|
74
84
|
}
|
|
75
85
|
async listCatalog(query) {
|
|
76
86
|
for (const dbPath of await codexStateDbsNewestFirst(this.codexHome)) {
|
|
@@ -181,261 +191,275 @@ export class CodexSessionAdapter {
|
|
|
181
191
|
}
|
|
182
192
|
}
|
|
183
193
|
}
|
|
184
|
-
function convertCodexRollout(
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
194
|
+
async function convertCodexRollout(path, expectedSessionId, fallbackName, fallbackCwd, limits) {
|
|
195
|
+
const converter = new CodexRolloutConverter(expectedSessionId, fallbackName, fallbackCwd, limits);
|
|
196
|
+
for await (const record of readCodexRolloutRecords(path, expectedSessionId, limits)) {
|
|
197
|
+
converter.accept(record);
|
|
198
|
+
}
|
|
199
|
+
return converter.finish();
|
|
200
|
+
}
|
|
201
|
+
class CodexRolloutConverter {
|
|
202
|
+
expectedSessionId;
|
|
203
|
+
fallbackName;
|
|
204
|
+
fallbackCwd;
|
|
205
|
+
limits;
|
|
206
|
+
messages = [];
|
|
207
|
+
failedTurnIds = new Set();
|
|
208
|
+
activeTurnId;
|
|
209
|
+
activeTurnIsExplicit = false;
|
|
210
|
+
activeModel = 'codex';
|
|
211
|
+
lastTimestamp = 0;
|
|
212
|
+
firstUserText;
|
|
213
|
+
metaCwd = '';
|
|
214
|
+
hasSessionMeta = false;
|
|
215
|
+
convertedBytes = 0;
|
|
216
|
+
constructor(expectedSessionId, fallbackName, fallbackCwd, limits) {
|
|
217
|
+
this.expectedSessionId = expectedSessionId;
|
|
218
|
+
this.fallbackName = fallbackName;
|
|
219
|
+
this.fallbackCwd = fallbackCwd;
|
|
220
|
+
this.limits = limits;
|
|
221
|
+
}
|
|
222
|
+
accept(record) {
|
|
213
223
|
const envelope = record.value;
|
|
224
|
+
if (envelope.type === 'session_meta' && !this.hasSessionMeta) {
|
|
225
|
+
this.hasSessionMeta = true;
|
|
226
|
+
const metaPayload = asRecord(envelope.payload);
|
|
227
|
+
const actualSessionId = stringField(metaPayload, 'session_id') ?? stringField(metaPayload, 'id');
|
|
228
|
+
if (actualSessionId !== this.expectedSessionId) {
|
|
229
|
+
throw new Error(`Codex rollout Session id mismatch: expected ${this.expectedSessionId}`);
|
|
230
|
+
}
|
|
231
|
+
this.metaCwd = safeCodexCwd(metaPayload?.cwd);
|
|
232
|
+
this.activeModel = stringField(metaPayload, 'model_provider') ?? this.activeModel;
|
|
233
|
+
const timestamp = normalizeEpochMs(envelope.timestamp);
|
|
234
|
+
if (timestamp !== undefined)
|
|
235
|
+
this.lastTimestamp = Math.max(this.lastTimestamp, timestamp);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
214
238
|
const payload = asRecord(envelope.payload);
|
|
215
239
|
if (!payload)
|
|
216
|
-
|
|
240
|
+
return;
|
|
217
241
|
if (envelope.type === 'turn_context') {
|
|
218
|
-
activeTurnId = stringField(payload, 'turn_id') ?? activeTurnId;
|
|
219
|
-
activeModel = stringField(payload, 'model') ?? activeModel;
|
|
220
|
-
|
|
242
|
+
this.activeTurnId = stringField(payload, 'turn_id') ?? this.activeTurnId;
|
|
243
|
+
this.activeModel = stringField(payload, 'model') ?? this.activeModel;
|
|
244
|
+
return;
|
|
221
245
|
}
|
|
222
246
|
if (envelope.type === 'event_msg') {
|
|
223
247
|
const eventType = stringField(payload, 'type');
|
|
224
248
|
if (eventType === 'task_started' || eventType === 'turn_started') {
|
|
225
249
|
const turnId = stringField(payload, 'turn_id');
|
|
226
250
|
if (turnId) {
|
|
227
|
-
activeTurnId = turnId;
|
|
228
|
-
activeTurnIsExplicit = true;
|
|
251
|
+
this.activeTurnId = turnId;
|
|
252
|
+
this.activeTurnIsExplicit = true;
|
|
229
253
|
}
|
|
230
|
-
|
|
254
|
+
return;
|
|
231
255
|
}
|
|
232
256
|
if (eventType === 'item_completed') {
|
|
233
257
|
const item = asRecord(payload.item);
|
|
234
258
|
const itemType = stringField(item, 'type')?.toLowerCase();
|
|
235
259
|
const eventTurnId = stringField(payload, 'turn_id');
|
|
236
260
|
if (eventTurnId) {
|
|
237
|
-
activeTurnId = eventTurnId;
|
|
238
|
-
activeTurnIsExplicit = true;
|
|
261
|
+
this.activeTurnId = eventTurnId;
|
|
262
|
+
this.activeTurnIsExplicit = true;
|
|
239
263
|
}
|
|
240
264
|
if (itemType === 'usermessage') {
|
|
241
|
-
if (!activeTurnIsExplicit) {
|
|
242
|
-
activeTurnId = generatedCodexId(expectedSessionId, 'turn', record.line);
|
|
265
|
+
if (!this.activeTurnIsExplicit) {
|
|
266
|
+
this.activeTurnId = generatedCodexId(this.expectedSessionId, 'turn', record.line);
|
|
243
267
|
}
|
|
244
268
|
const text = codexCompletedItemText(item) || codexCompletedItemMediaText(item);
|
|
245
269
|
if (text.length === 0)
|
|
246
|
-
|
|
247
|
-
firstUserText ??= text;
|
|
248
|
-
|
|
270
|
+
return;
|
|
271
|
+
this.firstUserText ??= text;
|
|
272
|
+
this.append({
|
|
249
273
|
type: 'user',
|
|
250
274
|
id: stringField(item, 'client_id') ??
|
|
251
275
|
stringField(item, 'id') ??
|
|
252
|
-
generatedCodexId(expectedSessionId, 'user', record.line),
|
|
253
|
-
turnId: ensureTurnId(record.line),
|
|
254
|
-
ts: timestampFor(record),
|
|
276
|
+
generatedCodexId(this.expectedSessionId, 'user', record.line),
|
|
277
|
+
turnId: this.ensureTurnId(record.line),
|
|
278
|
+
ts: this.timestampFor(record),
|
|
255
279
|
text,
|
|
256
280
|
});
|
|
257
|
-
|
|
281
|
+
return;
|
|
258
282
|
}
|
|
259
283
|
if (itemType === 'agentmessage') {
|
|
260
284
|
const text = codexCompletedItemText(item);
|
|
261
285
|
if (text.length === 0)
|
|
262
|
-
|
|
286
|
+
return;
|
|
263
287
|
const providerOptions = codexAssistantProviderOptions(item);
|
|
264
|
-
|
|
288
|
+
this.append({
|
|
265
289
|
type: 'assistant',
|
|
266
290
|
id: stringField(item, 'id') ??
|
|
267
|
-
generatedCodexId(expectedSessionId, 'assistant', record.line),
|
|
268
|
-
turnId: ensureTurnId(record.line),
|
|
269
|
-
ts: timestampFor(record),
|
|
291
|
+
generatedCodexId(this.expectedSessionId, 'assistant', record.line),
|
|
292
|
+
turnId: this.ensureTurnId(record.line),
|
|
293
|
+
ts: this.timestampFor(record),
|
|
270
294
|
text,
|
|
271
295
|
...(providerOptions !== undefined ? { providerOptions } : {}),
|
|
272
|
-
modelId: activeModel,
|
|
296
|
+
modelId: this.activeModel,
|
|
273
297
|
contentOrder: ['text'],
|
|
274
298
|
});
|
|
275
|
-
|
|
299
|
+
return;
|
|
276
300
|
}
|
|
277
301
|
if (itemType === 'reasoning') {
|
|
278
302
|
const reasoning = codexCompletedReasoningText(item);
|
|
279
303
|
if (reasoning.length === 0)
|
|
280
|
-
|
|
281
|
-
|
|
304
|
+
return;
|
|
305
|
+
this.append({
|
|
282
306
|
type: 'assistant',
|
|
283
307
|
id: stringField(item, 'id') ??
|
|
284
|
-
generatedCodexId(expectedSessionId, 'reasoning', record.line),
|
|
285
|
-
turnId: ensureTurnId(record.line),
|
|
286
|
-
ts: timestampFor(record),
|
|
308
|
+
generatedCodexId(this.expectedSessionId, 'reasoning', record.line),
|
|
309
|
+
turnId: this.ensureTurnId(record.line),
|
|
310
|
+
ts: this.timestampFor(record),
|
|
287
311
|
text: '',
|
|
288
312
|
thinking: { text: reasoning },
|
|
289
313
|
contentOrder: ['thinking'],
|
|
290
|
-
modelId: activeModel,
|
|
314
|
+
modelId: this.activeModel,
|
|
291
315
|
});
|
|
292
|
-
|
|
316
|
+
return;
|
|
293
317
|
}
|
|
294
318
|
}
|
|
295
319
|
if (eventType === 'user_message') {
|
|
296
|
-
if (!activeTurnIsExplicit) {
|
|
297
|
-
activeTurnId = generatedCodexId(expectedSessionId, 'turn', record.line);
|
|
320
|
+
if (!this.activeTurnIsExplicit) {
|
|
321
|
+
this.activeTurnId = generatedCodexId(this.expectedSessionId, 'turn', record.line);
|
|
298
322
|
}
|
|
299
323
|
const text = stringField(payload, 'message') ?? mediaOnlyUserText(payload);
|
|
300
324
|
if (text.length === 0)
|
|
301
|
-
|
|
302
|
-
firstUserText ??= text;
|
|
303
|
-
const turnId = ensureTurnId(record.line);
|
|
304
|
-
|
|
325
|
+
return;
|
|
326
|
+
this.firstUserText ??= text;
|
|
327
|
+
const turnId = this.ensureTurnId(record.line);
|
|
328
|
+
this.append({
|
|
305
329
|
type: 'user',
|
|
306
330
|
id: stringField(payload, 'client_id') ??
|
|
307
|
-
generatedCodexId(expectedSessionId, 'user', record.line),
|
|
331
|
+
generatedCodexId(this.expectedSessionId, 'user', record.line),
|
|
308
332
|
turnId,
|
|
309
|
-
ts: timestampFor(record),
|
|
333
|
+
ts: this.timestampFor(record),
|
|
310
334
|
text,
|
|
311
335
|
});
|
|
312
|
-
|
|
336
|
+
return;
|
|
313
337
|
}
|
|
314
338
|
if (eventType === 'agent_message') {
|
|
315
339
|
const text = stringField(payload, 'message');
|
|
316
340
|
if (!text)
|
|
317
|
-
|
|
341
|
+
return;
|
|
318
342
|
const providerOptions = codexAssistantProviderOptions(payload);
|
|
319
|
-
|
|
343
|
+
this.append({
|
|
320
344
|
type: 'assistant',
|
|
321
|
-
id: generatedCodexId(expectedSessionId, 'assistant', record.line),
|
|
322
|
-
turnId: ensureTurnId(record.line),
|
|
323
|
-
ts: timestampFor(record),
|
|
345
|
+
id: generatedCodexId(this.expectedSessionId, 'assistant', record.line),
|
|
346
|
+
turnId: this.ensureTurnId(record.line),
|
|
347
|
+
ts: this.timestampFor(record),
|
|
324
348
|
text,
|
|
325
349
|
...(providerOptions !== undefined ? { providerOptions } : {}),
|
|
326
|
-
modelId: activeModel,
|
|
350
|
+
modelId: this.activeModel,
|
|
327
351
|
contentOrder: ['text'],
|
|
328
352
|
});
|
|
329
|
-
|
|
353
|
+
return;
|
|
330
354
|
}
|
|
331
355
|
if (eventType === 'agent_reasoning') {
|
|
332
356
|
const reasoning = stringField(payload, 'text');
|
|
333
357
|
if (!reasoning)
|
|
334
|
-
|
|
335
|
-
|
|
358
|
+
return;
|
|
359
|
+
this.append({
|
|
336
360
|
type: 'assistant',
|
|
337
|
-
id: generatedCodexId(expectedSessionId, 'reasoning', record.line),
|
|
338
|
-
turnId: ensureTurnId(record.line),
|
|
339
|
-
ts: timestampFor(record),
|
|
361
|
+
id: generatedCodexId(this.expectedSessionId, 'reasoning', record.line),
|
|
362
|
+
turnId: this.ensureTurnId(record.line),
|
|
363
|
+
ts: this.timestampFor(record),
|
|
340
364
|
text: '',
|
|
341
365
|
thinking: { text: reasoning },
|
|
342
366
|
contentOrder: ['thinking'],
|
|
343
|
-
modelId: activeModel,
|
|
367
|
+
modelId: this.activeModel,
|
|
344
368
|
});
|
|
345
|
-
|
|
369
|
+
return;
|
|
346
370
|
}
|
|
347
371
|
if (eventType === 'context_compacted') {
|
|
348
|
-
|
|
372
|
+
this.append({
|
|
349
373
|
type: 'system_note',
|
|
350
|
-
id: generatedCodexId(expectedSessionId, 'compact', record.line),
|
|
351
|
-
turnId: activeTurnId,
|
|
352
|
-
ts: timestampFor(record),
|
|
374
|
+
id: generatedCodexId(this.expectedSessionId, 'compact', record.line),
|
|
375
|
+
turnId: this.activeTurnId,
|
|
376
|
+
ts: this.timestampFor(record),
|
|
353
377
|
kind: 'context_compacted',
|
|
354
378
|
});
|
|
355
|
-
|
|
379
|
+
return;
|
|
356
380
|
}
|
|
357
381
|
if (eventType === 'error') {
|
|
358
|
-
if (activeTurnId && codexErrorAffectsTurnStatus(payload)) {
|
|
359
|
-
failedTurnIds.add(activeTurnId);
|
|
382
|
+
if (this.activeTurnId && codexErrorAffectsTurnStatus(payload)) {
|
|
383
|
+
this.failedTurnIds.add(this.activeTurnId);
|
|
360
384
|
}
|
|
361
|
-
|
|
385
|
+
this.append({
|
|
362
386
|
type: 'system_note',
|
|
363
|
-
id: generatedCodexId(expectedSessionId, 'error', record.line),
|
|
364
|
-
turnId: activeTurnId,
|
|
365
|
-
ts: timestampFor(record),
|
|
387
|
+
id: generatedCodexId(this.expectedSessionId, 'error', record.line),
|
|
388
|
+
turnId: this.activeTurnId,
|
|
389
|
+
ts: this.timestampFor(record),
|
|
366
390
|
kind: 'error',
|
|
367
391
|
data: JSON.parse(JSON.stringify(payload)),
|
|
368
392
|
});
|
|
369
|
-
|
|
393
|
+
return;
|
|
370
394
|
}
|
|
371
395
|
if (eventType === 'task_complete' || eventType === 'turn_complete') {
|
|
372
|
-
const turnId = stringField(payload, 'turn_id') ?? ensureTurnId(record.line);
|
|
373
|
-
const failed = failedTurnIds.has(turnId) || payload.error != null;
|
|
374
|
-
|
|
396
|
+
const turnId = stringField(payload, 'turn_id') ?? this.ensureTurnId(record.line);
|
|
397
|
+
const failed = this.failedTurnIds.has(turnId) || payload.error != null;
|
|
398
|
+
this.append({
|
|
375
399
|
type: 'turn_state',
|
|
376
|
-
id: generatedCodexId(expectedSessionId, 'turn-state', record.line),
|
|
400
|
+
id: generatedCodexId(this.expectedSessionId, 'turn-state', record.line),
|
|
377
401
|
turnId,
|
|
378
|
-
ts: timestampFor(record),
|
|
402
|
+
ts: this.timestampFor(record),
|
|
379
403
|
status: failed ? 'failed' : 'completed',
|
|
380
404
|
...(failed ? { errorClass: 'codex_error' } : {}),
|
|
381
405
|
});
|
|
382
|
-
failedTurnIds.delete(turnId);
|
|
383
|
-
if (activeTurnId === turnId) {
|
|
384
|
-
activeTurnId = undefined;
|
|
385
|
-
activeTurnIsExplicit = false;
|
|
406
|
+
this.failedTurnIds.delete(turnId);
|
|
407
|
+
if (this.activeTurnId === turnId) {
|
|
408
|
+
this.activeTurnId = undefined;
|
|
409
|
+
this.activeTurnIsExplicit = false;
|
|
386
410
|
}
|
|
387
|
-
|
|
411
|
+
return;
|
|
388
412
|
}
|
|
389
413
|
if (eventType === 'turn_aborted') {
|
|
390
|
-
const turnId = stringField(payload, 'turn_id') ?? ensureTurnId(record.line);
|
|
391
|
-
const ts = timestampFor(record);
|
|
392
|
-
|
|
414
|
+
const turnId = stringField(payload, 'turn_id') ?? this.ensureTurnId(record.line);
|
|
415
|
+
const ts = this.timestampFor(record);
|
|
416
|
+
this.append({
|
|
393
417
|
type: 'turn_state',
|
|
394
|
-
id: generatedCodexId(expectedSessionId, 'turn-state', record.line),
|
|
418
|
+
id: generatedCodexId(this.expectedSessionId, 'turn-state', record.line),
|
|
395
419
|
turnId,
|
|
396
420
|
ts,
|
|
397
421
|
status: 'aborted',
|
|
398
422
|
abortedAt: normalizeEpochMs(payload.completed_at) ?? ts,
|
|
399
423
|
abortSource: stringField(payload, 'reason') ?? 'codex',
|
|
400
424
|
});
|
|
401
|
-
if (activeTurnId === turnId) {
|
|
402
|
-
activeTurnId = undefined;
|
|
403
|
-
activeTurnIsExplicit = false;
|
|
425
|
+
if (this.activeTurnId === turnId) {
|
|
426
|
+
this.activeTurnId = undefined;
|
|
427
|
+
this.activeTurnIsExplicit = false;
|
|
404
428
|
}
|
|
405
|
-
|
|
429
|
+
return;
|
|
406
430
|
}
|
|
407
431
|
}
|
|
408
432
|
if (envelope.type !== 'response_item')
|
|
409
|
-
|
|
433
|
+
return;
|
|
410
434
|
const itemType = stringField(payload, 'type');
|
|
411
435
|
if (itemType === 'function_call' || itemType === 'custom_tool_call') {
|
|
412
436
|
const callId = stringField(payload, 'call_id');
|
|
413
437
|
const toolName = namespacedToolName(payload);
|
|
414
438
|
if (!callId || !toolName)
|
|
415
|
-
|
|
439
|
+
return;
|
|
416
440
|
const rawArgs = itemType === 'function_call'
|
|
417
441
|
? stringField(payload, 'arguments')
|
|
418
442
|
: stringField(payload, 'input');
|
|
419
|
-
|
|
443
|
+
this.append({
|
|
420
444
|
type: 'tool_call',
|
|
421
445
|
id: callId,
|
|
422
|
-
turnId: ensureTurnId(record.line),
|
|
423
|
-
ts: timestampFor(record),
|
|
446
|
+
turnId: this.ensureTurnId(record.line),
|
|
447
|
+
ts: this.timestampFor(record),
|
|
424
448
|
toolName,
|
|
425
449
|
args: parseJsonString(rawArgs),
|
|
426
450
|
});
|
|
427
|
-
|
|
451
|
+
return;
|
|
428
452
|
}
|
|
429
453
|
if (itemType === 'function_call_output' || itemType === 'custom_tool_call_output') {
|
|
430
454
|
const callId = stringField(payload, 'call_id');
|
|
431
455
|
if (!callId)
|
|
432
|
-
|
|
433
|
-
|
|
456
|
+
return;
|
|
457
|
+
this.append({
|
|
434
458
|
type: 'tool_result',
|
|
435
459
|
id: stringField(payload, 'id') ??
|
|
436
|
-
generatedCodexId(expectedSessionId, 'tool-result', record.line),
|
|
437
|
-
turnId: ensureTurnId(record.line),
|
|
438
|
-
ts: timestampFor(record),
|
|
460
|
+
generatedCodexId(this.expectedSessionId, 'tool-result', record.line),
|
|
461
|
+
turnId: this.ensureTurnId(record.line),
|
|
462
|
+
ts: this.timestampFor(record),
|
|
439
463
|
toolUseId: callId,
|
|
440
464
|
// Codex persists the output body but not FunctionCallOutputPayload.success.
|
|
441
465
|
// Preserve the raw body and avoid guessing failure from its text.
|
|
@@ -444,37 +468,134 @@ function convertCodexRollout(text, expectedSessionId, fallbackName, fallbackCwd)
|
|
|
444
468
|
});
|
|
445
469
|
}
|
|
446
470
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
471
|
+
finish() {
|
|
472
|
+
if (!this.hasSessionMeta) {
|
|
473
|
+
throw new Error(`Codex rollout Session id mismatch: expected ${this.expectedSessionId}`);
|
|
474
|
+
}
|
|
475
|
+
const name = sanitizeForeignTitle(this.fallbackName) ||
|
|
476
|
+
sanitizeForeignTitle(this.firstUserText) ||
|
|
477
|
+
this.expectedSessionId;
|
|
478
|
+
return {
|
|
479
|
+
sourceSessionId: this.expectedSessionId,
|
|
480
|
+
metadata: { name, cwd: this.metaCwd || this.fallbackCwd },
|
|
481
|
+
messages: this.messages,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
timestampFor(record) {
|
|
485
|
+
const parsed = normalizeEpochMs(record.value.timestamp);
|
|
486
|
+
if (parsed !== undefined)
|
|
487
|
+
this.lastTimestamp = Math.max(this.lastTimestamp, parsed);
|
|
488
|
+
else
|
|
489
|
+
this.lastTimestamp += 1;
|
|
490
|
+
return parsed ?? this.lastTimestamp;
|
|
491
|
+
}
|
|
492
|
+
ensureTurnId(line) {
|
|
493
|
+
this.activeTurnId ??= generatedCodexId(this.expectedSessionId, 'turn', line);
|
|
494
|
+
return this.activeTurnId;
|
|
495
|
+
}
|
|
496
|
+
append(message) {
|
|
497
|
+
if (this.messages.length >= this.limits.maxMessages) {
|
|
498
|
+
throw new Error(`Codex rollout converts to more than ${this.limits.maxMessages} messages`);
|
|
499
|
+
}
|
|
500
|
+
const encodedBytes = Buffer.byteLength(JSON.stringify(message), 'utf8');
|
|
501
|
+
if (encodedBytes > this.limits.maxConvertedBytes - this.convertedBytes) {
|
|
502
|
+
throw new Error(`Codex rollout converts to more than ${this.limits.maxConvertedBytes} bytes`);
|
|
503
|
+
}
|
|
504
|
+
this.convertedBytes += encodedBytes;
|
|
505
|
+
this.messages.push(message);
|
|
506
|
+
}
|
|
453
507
|
}
|
|
454
|
-
function
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (line.trim().length === 0)
|
|
463
|
-
continue;
|
|
464
|
-
try {
|
|
465
|
-
const value = JSON.parse(line);
|
|
466
|
-
if (!isRecord(value))
|
|
467
|
-
throw new Error('record is not an object');
|
|
468
|
-
records.push({ line: index + 1, value });
|
|
508
|
+
async function* readCodexRolloutRecords(path, sessionId, limits) {
|
|
509
|
+
const handle = await open(path, 'r');
|
|
510
|
+
try {
|
|
511
|
+
const metadata = await handle.stat();
|
|
512
|
+
if (!metadata.isFile())
|
|
513
|
+
throw new Error('Codex rollout is not a regular file');
|
|
514
|
+
if (metadata.size > limits.maxRolloutBytes) {
|
|
515
|
+
throw new Error(`Codex rollout exceeds ${limits.maxRolloutBytes} bytes`);
|
|
469
516
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
517
|
+
const snapshotBytes = metadata.size;
|
|
518
|
+
const pending = [];
|
|
519
|
+
let pendingBytes = 0;
|
|
520
|
+
let observedBytes = 0;
|
|
521
|
+
let line = 0;
|
|
522
|
+
if (snapshotBytes > 0) {
|
|
523
|
+
for await (const value of handle.createReadStream({
|
|
524
|
+
autoClose: false,
|
|
525
|
+
emitClose: false,
|
|
526
|
+
start: 0,
|
|
527
|
+
end: snapshotBytes - 1,
|
|
528
|
+
highWaterMark: CODEX_ROLLOUT_READ_BYTES,
|
|
529
|
+
})) {
|
|
530
|
+
const chunk = Buffer.from(value);
|
|
531
|
+
observedBytes += chunk.byteLength;
|
|
532
|
+
if (observedBytes > snapshotBytes) {
|
|
533
|
+
throw new Error('Codex rollout changed while being read');
|
|
534
|
+
}
|
|
535
|
+
let start = 0;
|
|
536
|
+
for (;;) {
|
|
537
|
+
const newline = chunk.indexOf(0x0a, start);
|
|
538
|
+
if (newline === -1)
|
|
539
|
+
break;
|
|
540
|
+
const segment = chunk.subarray(start, newline);
|
|
541
|
+
assertCodexRecordSize(pendingBytes + segment.byteLength, limits.maxRecordBytes, line + 1);
|
|
542
|
+
line += 1;
|
|
543
|
+
const record = parseCodexRolloutLine(pending.length === 0
|
|
544
|
+
? segment
|
|
545
|
+
: Buffer.concat([...pending, segment], pendingBytes + segment.byteLength), sessionId, line, false);
|
|
546
|
+
if (record)
|
|
547
|
+
yield record;
|
|
548
|
+
pending.length = 0;
|
|
549
|
+
pendingBytes = 0;
|
|
550
|
+
start = newline + 1;
|
|
551
|
+
}
|
|
552
|
+
if (start < chunk.byteLength) {
|
|
553
|
+
const segment = chunk.subarray(start);
|
|
554
|
+
assertCodexRecordSize(pendingBytes + segment.byteLength, limits.maxRecordBytes, line + 1);
|
|
555
|
+
pending.push(segment);
|
|
556
|
+
pendingBytes += segment.byteLength;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (observedBytes !== snapshotBytes)
|
|
561
|
+
throw new Error('Codex rollout changed while being read');
|
|
562
|
+
if (pendingBytes > 0) {
|
|
563
|
+
line += 1;
|
|
564
|
+
const record = parseCodexRolloutLine(pending.length === 1 ? pending[0] : Buffer.concat(pending, pendingBytes), sessionId, line, true);
|
|
565
|
+
if (record)
|
|
566
|
+
yield record;
|
|
475
567
|
}
|
|
476
568
|
}
|
|
477
|
-
|
|
569
|
+
finally {
|
|
570
|
+
await handle.close();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
function parseCodexRolloutLine(bytes, sessionId, line, tolerateTornTail) {
|
|
574
|
+
const text = bytes.toString('utf8');
|
|
575
|
+
if (text.trim().length === 0)
|
|
576
|
+
return undefined;
|
|
577
|
+
try {
|
|
578
|
+
const value = JSON.parse(text);
|
|
579
|
+
if (!isRecord(value))
|
|
580
|
+
throw new Error('record is not an object');
|
|
581
|
+
return { line, value };
|
|
582
|
+
}
|
|
583
|
+
catch (error) {
|
|
584
|
+
if (tolerateTornTail)
|
|
585
|
+
return undefined;
|
|
586
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
587
|
+
throw new Error(`Invalid Codex rollout ${sessionId} at line ${line}: ${detail}`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function assertCodexRecordSize(actualBytes, maxBytes, line) {
|
|
591
|
+
if (actualBytes > maxBytes) {
|
|
592
|
+
throw new Error(`Codex rollout record at line ${line} exceeds ${maxBytes} bytes`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function assertPositiveSafeInteger(value, label) {
|
|
596
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
597
|
+
throw new Error(`${label} must be a positive safe integer`);
|
|
598
|
+
}
|
|
478
599
|
}
|
|
479
600
|
function catalogEntryFromRolloutHead(text, candidate) {
|
|
480
601
|
const lines = text.split('\n');
|
|
@@ -644,39 +765,6 @@ async function walkRolloutFiles(root, archived) {
|
|
|
644
765
|
await visit(root);
|
|
645
766
|
return files;
|
|
646
767
|
}
|
|
647
|
-
async function readBoundedUtf8File(path, maxBytes) {
|
|
648
|
-
const handle = await open(path, 'r');
|
|
649
|
-
try {
|
|
650
|
-
const metadata = await handle.stat();
|
|
651
|
-
if (!metadata.isFile())
|
|
652
|
-
throw new Error('Codex rollout is not a regular file');
|
|
653
|
-
if (metadata.size > maxBytes)
|
|
654
|
-
throw new Error(`Codex rollout exceeds ${maxBytes} bytes`);
|
|
655
|
-
const chunks = [];
|
|
656
|
-
let total = 0;
|
|
657
|
-
for (;;) {
|
|
658
|
-
const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1 - total));
|
|
659
|
-
const { bytesRead } = await handle.read(buffer, 0, buffer.length, total);
|
|
660
|
-
if (bytesRead === 0)
|
|
661
|
-
break;
|
|
662
|
-
total += bytesRead;
|
|
663
|
-
if (total > maxBytes)
|
|
664
|
-
throw new Error(`Codex rollout exceeds ${maxBytes} bytes`);
|
|
665
|
-
chunks.push(buffer.subarray(0, bytesRead));
|
|
666
|
-
if (total === maxBytes) {
|
|
667
|
-
const probe = Buffer.allocUnsafe(1);
|
|
668
|
-
if ((await handle.read(probe, 0, 1, total)).bytesRead > 0) {
|
|
669
|
-
throw new Error(`Codex rollout exceeds ${maxBytes} bytes`);
|
|
670
|
-
}
|
|
671
|
-
break;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
return Buffer.concat(chunks, total).toString('utf8');
|
|
675
|
-
}
|
|
676
|
-
finally {
|
|
677
|
-
await handle.close();
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
768
|
async function readUtf8Prefix(path, maxBytes) {
|
|
681
769
|
const handle = await open(path, 'r');
|
|
682
770
|
try {
|