@orion-agents/orion-code 0.2.0 → 0.3.0
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/CHANGELOG.md +75 -0
- package/README.md +73 -14
- package/README.zh-CN.md +63 -13
- package/bin/orion +15 -4
- package/dist/cli.js +51 -172
- package/dist/cli.js.map +1 -1
- package/dist/commands/model-command-handlers.d.ts +2 -2
- package/dist/commands/model-command-handlers.d.ts.map +1 -1
- package/dist/commands/model-command-handlers.js +113 -32
- package/dist/commands/model-command-handlers.js.map +1 -1
- package/dist/commands/session-command-handlers.d.ts +1 -1
- package/dist/commands/session-command-handlers.d.ts.map +1 -1
- package/dist/commands/session-command-handlers.js +121 -16
- package/dist/commands/session-command-handlers.js.map +1 -1
- package/dist/commands/types.d.ts +14 -2
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/runtime/agent-loop.d.ts +2 -0
- package/dist/runtime/agent-loop.d.ts.map +1 -1
- package/dist/runtime/agent-loop.js +4 -3
- package/dist/runtime/agent-loop.js.map +1 -1
- package/dist/runtime/agent-runtime-controller.d.ts +32 -1
- package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-controller.js +250 -31
- package/dist/runtime/agent-runtime-controller.js.map +1 -1
- package/dist/runtime/agent-runtime-runner.d.ts +3 -0
- package/dist/runtime/agent-runtime-runner.d.ts.map +1 -1
- package/dist/runtime/goal-runtime-coordinator.d.ts +6 -0
- package/dist/runtime/goal-runtime-coordinator.d.ts.map +1 -1
- package/dist/runtime/goal-runtime-coordinator.js +5 -0
- package/dist/runtime/goal-runtime-coordinator.js.map +1 -1
- package/dist/runtime/legacy-thread-materializer.d.ts +18 -0
- package/dist/runtime/legacy-thread-materializer.d.ts.map +1 -1
- package/dist/runtime/legacy-thread-materializer.js +27 -12
- package/dist/runtime/legacy-thread-materializer.js.map +1 -1
- package/dist/runtime/orion-runtime-v1.d.ts +3 -0
- package/dist/runtime/orion-runtime-v1.d.ts.map +1 -1
- package/dist/runtime/orion-runtime-v1.js +8 -1
- package/dist/runtime/orion-runtime-v1.js.map +1 -1
- package/dist/runtime/orion-session-runner.d.ts +7 -1
- package/dist/runtime/orion-session-runner.d.ts.map +1 -1
- package/dist/runtime/orion-session-runner.js +35 -3
- package/dist/runtime/orion-session-runner.js.map +1 -1
- package/dist/runtime/product-bootstrap.d.ts +22 -0
- package/dist/runtime/product-bootstrap.d.ts.map +1 -0
- package/dist/runtime/product-bootstrap.js +374 -0
- package/dist/runtime/product-bootstrap.js.map +1 -0
- package/dist/runtime/product-orion-runtime.d.ts +2 -1
- package/dist/runtime/product-orion-runtime.d.ts.map +1 -1
- package/dist/runtime/product-orion-runtime.js +114 -16
- package/dist/runtime/product-orion-runtime.js.map +1 -1
- package/dist/runtime/protocol/runtime-protocol-v1.d.ts +1 -1
- package/dist/runtime/protocol/runtime-protocol-v1.js +1 -1
- package/dist/runtime/protocol/runtime-protocol-v1.js.map +1 -1
- package/dist/runtime/release-receipts.d.ts +63 -2
- package/dist/runtime/release-receipts.d.ts.map +1 -1
- package/dist/runtime/release-receipts.js +298 -7
- package/dist/runtime/release-receipts.js.map +1 -1
- package/dist/runtime/session-history-recovery.d.ts +19 -0
- package/dist/runtime/session-history-recovery.d.ts.map +1 -0
- package/dist/runtime/session-history-recovery.js +45 -0
- package/dist/runtime/session-history-recovery.js.map +1 -0
- package/dist/runtime/step-snapshot.d.ts.map +1 -1
- package/dist/runtime/step-snapshot.js +22 -2
- package/dist/runtime/step-snapshot.js.map +1 -1
- package/dist/runtime/subagents/runtime-integration.d.ts +1 -1
- package/dist/runtime/subagents/runtime-integration.d.ts.map +1 -1
- package/dist/runtime/subagents/runtime-integration.js +5 -2
- package/dist/runtime/subagents/runtime-integration.js.map +1 -1
- package/dist/runtime/thread-event-store.d.ts +27 -0
- package/dist/runtime/thread-event-store.d.ts.map +1 -1
- package/dist/runtime/thread-event-store.js +119 -17
- package/dist/runtime/thread-event-store.js.map +1 -1
- package/dist/runtime/thread-projection.d.ts +10 -0
- package/dist/runtime/thread-projection.d.ts.map +1 -1
- package/dist/runtime/thread-projection.js +40 -0
- package/dist/runtime/thread-projection.js.map +1 -1
- package/dist/runtime/thread-runtime.d.ts +3 -1
- package/dist/runtime/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/thread-runtime.js +6 -0
- package/dist/runtime/thread-runtime.js.map +1 -1
- package/dist/runtime/thread-session-view.d.ts +88 -0
- package/dist/runtime/thread-session-view.d.ts.map +1 -0
- package/dist/runtime/thread-session-view.js +327 -0
- package/dist/runtime/thread-session-view.js.map +1 -0
- package/dist/runtime/thread-ui-adapter.d.ts +4 -0
- package/dist/runtime/thread-ui-adapter.d.ts.map +1 -1
- package/dist/runtime/thread-ui-adapter.js +193 -5
- package/dist/runtime/thread-ui-adapter.js.map +1 -1
- package/dist/runtime/ui-events.d.ts +37 -0
- package/dist/runtime/ui-events.d.ts.map +1 -1
- package/dist/runtime/ui-events.js +1 -0
- package/dist/runtime/ui-events.js.map +1 -1
- package/dist/runtime/ui-view-model.d.ts +3 -0
- package/dist/runtime/ui-view-model.d.ts.map +1 -1
- package/dist/runtime/ui-view-model.js +10 -3
- package/dist/runtime/ui-view-model.js.map +1 -1
- package/dist/services/file-lock.d.ts.map +1 -1
- package/dist/services/file-lock.js +42 -17
- package/dist/services/file-lock.js.map +1 -1
- package/dist/services/global-config.d.ts +25 -0
- package/dist/services/global-config.d.ts.map +1 -1
- package/dist/services/global-config.js +373 -5
- package/dist/services/global-config.js.map +1 -1
- package/dist/services/session-storage.d.ts +67 -0
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +327 -61
- package/dist/services/session-storage.js.map +1 -1
- package/dist/services/settings-coordinator.d.ts +174 -0
- package/dist/services/settings-coordinator.d.ts.map +1 -0
- package/dist/services/settings-coordinator.js +605 -0
- package/dist/services/settings-coordinator.js.map +1 -0
- package/dist/services/settings-document-repository.d.ts +138 -0
- package/dist/services/settings-document-repository.d.ts.map +1 -0
- package/dist/services/settings-document-repository.js +551 -0
- package/dist/services/settings-document-repository.js.map +1 -0
- package/dist/terminal-ui/launch.d.ts.map +1 -1
- package/dist/terminal-ui/launch.js +7 -0
- package/dist/terminal-ui/launch.js.map +1 -1
- package/dist/tools/web.js +1 -1
- package/dist/tools/web.js.map +1 -1
- package/dist/tui-ui/chrome-view-model.d.ts.map +1 -1
- package/dist/tui-ui/chrome-view-model.js +21 -7
- package/dist/tui-ui/chrome-view-model.js.map +1 -1
- package/dist/tui-ui/launch.d.ts.map +1 -1
- package/dist/tui-ui/launch.js +13 -0
- package/dist/tui-ui/launch.js.map +1 -1
- package/dist/tui-ui/state.d.ts.map +1 -1
- package/dist/tui-ui/state.js +7 -0
- package/dist/tui-ui/state.js.map +1 -1
- package/dist/web/event-hub.d.ts +39 -0
- package/dist/web/event-hub.d.ts.map +1 -0
- package/dist/web/event-hub.js +263 -0
- package/dist/web/event-hub.js.map +1 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +28 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/launch.d.ts +9 -0
- package/dist/web/launch.d.ts.map +1 -0
- package/dist/web/launch.js +64 -0
- package/dist/web/launch.js.map +1 -0
- package/dist/web/protocol.d.ts +346 -0
- package/dist/web/protocol.d.ts.map +1 -0
- package/dist/web/protocol.js +348 -0
- package/dist/web/protocol.js.map +1 -0
- package/dist/web/server.d.ts +20 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +498 -0
- package/dist/web/server.js.map +1 -0
- package/dist/web/workbench-controller.d.ts +63 -0
- package/dist/web/workbench-controller.d.ts.map +1 -0
- package/dist/web/workbench-controller.js +774 -0
- package/dist/web/workbench-controller.js.map +1 -0
- package/dist/web-client/assets/index-BAEO71Gp.css +1 -0
- package/dist/web-client/assets/index-DSkFy7gM.js +13 -0
- package/dist/web-client/index.html +16 -0
- package/docs/architecture/v0.3.0-web-api.yaml +1309 -0
- package/docs/migration/v0.2.2-to-v0.3.0-settings.md +114 -0
- package/docs/migration/v0.2.2-to-v0.3.0.md +55 -0
- package/docs/orion.example.json +9 -2
- package/docs/plan/v0.3.0-node-runtime-compatibility-plan.md +57 -0
- package/docs/plan/v0.3.0-settings-integration-plan.md +740 -0
- package/docs/plan/v0.3.0-web-workbench-plan.md +325 -0
- package/docs/readme.md +12 -1
- package/npm-shrinkwrap.json +1777 -424
- package/package.json +33 -9
|
@@ -15,6 +15,8 @@ exports.advanceSessionCompactSourceReceipt = advanceSessionCompactSourceReceipt;
|
|
|
15
15
|
exports.createSession = createSession;
|
|
16
16
|
exports.saveSessionMeta = saveSessionMeta;
|
|
17
17
|
exports.updateSessionEffort = updateSessionEffort;
|
|
18
|
+
exports.updateSessionModel = updateSessionModel;
|
|
19
|
+
exports.updateSessionThreadReadModel = updateSessionThreadReadModel;
|
|
18
20
|
exports.loadSessionMeta = loadSessionMeta;
|
|
19
21
|
exports.updateSessionStats = updateSessionStats;
|
|
20
22
|
exports.resumeSession = resumeSession;
|
|
@@ -32,6 +34,7 @@ exports.loadSessionTranscriptMessages = loadSessionTranscriptMessages;
|
|
|
32
34
|
exports.endSession = endSession;
|
|
33
35
|
exports.updateSessionSummary = updateSessionSummary;
|
|
34
36
|
exports.getLastSession = getLastSession;
|
|
37
|
+
exports.filterSessionsWithRestorableHistory = filterSessionsWithRestorableHistory;
|
|
35
38
|
exports.appendHistory = appendHistory;
|
|
36
39
|
exports.readHistory = readHistory;
|
|
37
40
|
exports.readProjectHistory = readProjectHistory;
|
|
@@ -44,10 +47,13 @@ exports.readSessionMessages = readSessionMessages;
|
|
|
44
47
|
exports.appendSessionTraceEvent = appendSessionTraceEvent;
|
|
45
48
|
exports.readSessionTraceEvents = readSessionTraceEvents;
|
|
46
49
|
exports.loadSessionHistory = loadSessionHistory;
|
|
50
|
+
exports.loadSessionHistoryWithDiagnostics = loadSessionHistoryWithDiagnostics;
|
|
51
|
+
exports.loadSessionRestoreBundle = loadSessionRestoreBundle;
|
|
47
52
|
exports.listSessions = listSessions;
|
|
48
53
|
exports.listProjectSessions = listProjectSessions;
|
|
49
54
|
exports.findSession = findSession;
|
|
50
55
|
exports.lookupSessionRef = lookupSessionRef;
|
|
56
|
+
exports.lookupSessionRefInSessions = lookupSessionRefInSessions;
|
|
51
57
|
exports.renameSession = renameSession;
|
|
52
58
|
exports.deleteSession = deleteSession;
|
|
53
59
|
const fs_1 = require("fs");
|
|
@@ -64,6 +70,10 @@ const debug_log_1 = require("../utils/debug-log");
|
|
|
64
70
|
const goal_storage_1 = require("./goal-storage");
|
|
65
71
|
const fingerprint_1 = require("./compact/fingerprint");
|
|
66
72
|
const token_estimate_1 = require("../utils/token-estimate");
|
|
73
|
+
const thread_session_view_1 = require("../runtime/thread-session-view");
|
|
74
|
+
const legacy_thread_materializer_1 = require("../runtime/legacy-thread-materializer");
|
|
75
|
+
const paths_1 = require("../product/paths");
|
|
76
|
+
const turn_commit_1 = require("../runtime/turn-commit");
|
|
67
77
|
const harness_1 = require("../harness");
|
|
68
78
|
var redaction_2 = require("./redaction");
|
|
69
79
|
Object.defineProperty(exports, "redactTraceText", { enumerable: true, get: function () { return redaction_2.redactTraceText; } });
|
|
@@ -302,7 +312,7 @@ function normalizeSessionMeta(session) {
|
|
|
302
312
|
updatedAt,
|
|
303
313
|
updatedAtIso: session.updatedAtIso ?? new Date(updatedAt).toISOString(),
|
|
304
314
|
messageCount: session.messageCount ?? 0,
|
|
305
|
-
historySizeBytes: computeSessionHistorySizeBytes({ id: session.id, projectPath }),
|
|
315
|
+
historySizeBytes: session.historySizeBytes ?? computeSessionHistorySizeBytes({ id: session.id, projectPath }),
|
|
306
316
|
tokenCount: session.tokenCount ?? 0,
|
|
307
317
|
cost: session.cost ?? 0,
|
|
308
318
|
gitBranch: session.gitBranch ?? getGitBranch(projectPath),
|
|
@@ -974,6 +984,10 @@ function readSessionMetaAtPath(path) {
|
|
|
974
984
|
const session = parseSessionMetaFile(path);
|
|
975
985
|
return session ? tryNormalizeSessionMeta(session, path) : null;
|
|
976
986
|
}
|
|
987
|
+
function loadRawSessionMeta(sessionId) {
|
|
988
|
+
const path = findSessionMetaPath(sessionId);
|
|
989
|
+
return path ? readSessionMetaAtPath(path) : null;
|
|
990
|
+
}
|
|
977
991
|
/**
|
|
978
992
|
* Serialize a session read-modify-write transaction across Orion processes.
|
|
979
993
|
* The metadata path is rediscovered before each transaction and re-read only
|
|
@@ -1008,12 +1022,100 @@ function updateSessionEffort(sessionId, effortPreference) {
|
|
|
1008
1022
|
}
|
|
1009
1023
|
});
|
|
1010
1024
|
}
|
|
1025
|
+
/** Persist the model selected for one Session without replacing unrelated metadata. */
|
|
1026
|
+
function updateSessionModel(sessionId, model) {
|
|
1027
|
+
const normalizedModel = model.trim();
|
|
1028
|
+
if (!normalizedModel)
|
|
1029
|
+
throw new Error('Session model must not be empty.');
|
|
1030
|
+
return mutateSessionMeta(sessionId, session => {
|
|
1031
|
+
session.model = normalizedModel;
|
|
1032
|
+
session.updatedAt = Date.now();
|
|
1033
|
+
session.updatedAtIso = new Date(session.updatedAt).toISOString();
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Persist the bounded Session-list projection derived from an authoritative
|
|
1038
|
+
* v2 Thread. List and picker paths consume this catalog projection directly;
|
|
1039
|
+
* they must never replay every Thread merely to render metadata.
|
|
1040
|
+
*/
|
|
1041
|
+
function updateSessionThreadReadModel(sessionId, input) {
|
|
1042
|
+
if (!input.threadId.trim())
|
|
1043
|
+
throw new Error('Session Thread id must not be empty.');
|
|
1044
|
+
if (!Number.isSafeInteger(input.cursor) || input.cursor < 0) {
|
|
1045
|
+
throw new Error('Session Thread cursor must be a non-negative safe integer.');
|
|
1046
|
+
}
|
|
1047
|
+
if (!input.projectionDigest.trim()) {
|
|
1048
|
+
throw new Error('Session Thread projection digest must not be empty.');
|
|
1049
|
+
}
|
|
1050
|
+
if (!Number.isSafeInteger(input.cutoverGeneration) || input.cutoverGeneration < 1) {
|
|
1051
|
+
throw new Error('Session Thread cutover generation must be a positive safe integer.');
|
|
1052
|
+
}
|
|
1053
|
+
if (input.lastRecordHash !== null && !/^[a-f0-9]{64}$/i.test(input.lastRecordHash)) {
|
|
1054
|
+
throw new Error('Session Thread last record hash must be a SHA-256 digest or null.');
|
|
1055
|
+
}
|
|
1056
|
+
for (const [name, value] of [
|
|
1057
|
+
['logDevice', input.logDevice],
|
|
1058
|
+
['logInode', input.logInode],
|
|
1059
|
+
['logMtimeNs', input.logMtimeNs],
|
|
1060
|
+
['logCtimeNs', input.logCtimeNs],
|
|
1061
|
+
]) {
|
|
1062
|
+
if (!/^\d+$/.test(value))
|
|
1063
|
+
throw new Error(`Session Thread ${name} must be an integer string.`);
|
|
1064
|
+
}
|
|
1065
|
+
if (!Number.isSafeInteger(input.messageCount) || input.messageCount < 0) {
|
|
1066
|
+
throw new Error('Session Thread messageCount must be a non-negative safe integer.');
|
|
1067
|
+
}
|
|
1068
|
+
if (!Number.isSafeInteger(input.historySizeBytes) || input.historySizeBytes < 0) {
|
|
1069
|
+
throw new Error('Session Thread historySizeBytes must be a non-negative safe integer.');
|
|
1070
|
+
}
|
|
1071
|
+
if (!Number.isFinite(input.updatedAt) || input.updatedAt < 0) {
|
|
1072
|
+
throw new Error('Session Thread updatedAt must be a non-negative finite timestamp.');
|
|
1073
|
+
}
|
|
1074
|
+
return withLockedSession(sessionId, session => {
|
|
1075
|
+
const nextUpdatedAt = Math.max(session.updatedAt ?? session.startTime, input.updatedAt);
|
|
1076
|
+
const current = session.threadReadModel;
|
|
1077
|
+
const unchanged = session.messageCount === input.messageCount &&
|
|
1078
|
+
session.historySizeBytes === input.historySizeBytes &&
|
|
1079
|
+
session.updatedAt === nextUpdatedAt &&
|
|
1080
|
+
current?.version === 1 &&
|
|
1081
|
+
current.threadId === input.threadId &&
|
|
1082
|
+
current.cursor === input.cursor &&
|
|
1083
|
+
current.projectionDigest === input.projectionDigest &&
|
|
1084
|
+
current.cutoverGeneration === input.cutoverGeneration &&
|
|
1085
|
+
current.lastRecordHash === input.lastRecordHash &&
|
|
1086
|
+
current.logDevice === input.logDevice &&
|
|
1087
|
+
current.logInode === input.logInode &&
|
|
1088
|
+
current.logMtimeNs === input.logMtimeNs &&
|
|
1089
|
+
current.logCtimeNs === input.logCtimeNs;
|
|
1090
|
+
if (unchanged)
|
|
1091
|
+
return session;
|
|
1092
|
+
session.messageCount = input.messageCount;
|
|
1093
|
+
session.historySizeBytes = input.historySizeBytes;
|
|
1094
|
+
session.threadReadModel = {
|
|
1095
|
+
version: 1,
|
|
1096
|
+
threadId: input.threadId,
|
|
1097
|
+
cursor: input.cursor,
|
|
1098
|
+
projectionDigest: input.projectionDigest,
|
|
1099
|
+
cutoverGeneration: input.cutoverGeneration,
|
|
1100
|
+
lastRecordHash: input.lastRecordHash,
|
|
1101
|
+
logDevice: input.logDevice,
|
|
1102
|
+
logInode: input.logInode,
|
|
1103
|
+
logMtimeNs: input.logMtimeNs,
|
|
1104
|
+
logCtimeNs: input.logCtimeNs,
|
|
1105
|
+
};
|
|
1106
|
+
// Selecting a Session is itself recent activity. A background projection
|
|
1107
|
+
// refresh must not reorder it backwards to the timestamp of its last fact.
|
|
1108
|
+
session.updatedAt = nextUpdatedAt;
|
|
1109
|
+
session.updatedAtIso = new Date(session.updatedAt).toISOString();
|
|
1110
|
+
writeSessionMetaUnlocked(session);
|
|
1111
|
+
return session;
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1011
1114
|
/**
|
|
1012
1115
|
* 加载会话元数据
|
|
1013
1116
|
*/
|
|
1014
1117
|
function loadSessionMeta(sessionId) {
|
|
1015
|
-
|
|
1016
|
-
return path ? readSessionMetaAtPath(path) : null;
|
|
1118
|
+
return loadRawSessionMeta(sessionId);
|
|
1017
1119
|
}
|
|
1018
1120
|
/**
|
|
1019
1121
|
* 更新会话统计
|
|
@@ -1030,11 +1132,19 @@ function updateSessionStats(sessionId, tokens, cost) {
|
|
|
1030
1132
|
* Mark a saved session as active again and refresh project metadata.
|
|
1031
1133
|
*/
|
|
1032
1134
|
function resumeSession(sessionId) {
|
|
1033
|
-
return
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1135
|
+
return withLockedSession(sessionId, current => {
|
|
1136
|
+
// Session selection is not new conversation activity. Once a Session is
|
|
1137
|
+
// already open, do not rewrite its metadata/catalog merely because the UI
|
|
1138
|
+
// selected it again. This keeps /resume off the fsync path while preserving
|
|
1139
|
+
// the one durable ended -> active transition.
|
|
1140
|
+
if (current.endTime === undefined)
|
|
1141
|
+
return current;
|
|
1142
|
+
current.endTime = undefined;
|
|
1143
|
+
current.gitBranch ?? (current.gitBranch = getGitBranch(current.projectPath));
|
|
1144
|
+
current.updatedAt = Date.now();
|
|
1145
|
+
current.updatedAtIso = new Date(current.updatedAt).toISOString();
|
|
1146
|
+
writeSessionMetaUnlocked(current);
|
|
1147
|
+
return current;
|
|
1038
1148
|
});
|
|
1039
1149
|
}
|
|
1040
1150
|
/** Keep the session metadata's additive Goal binding in sync with its sidecar. */
|
|
@@ -1110,30 +1220,7 @@ function loadSessionHarnessState(sessionId) {
|
|
|
1110
1220
|
const session = loadSessionMeta(sessionId);
|
|
1111
1221
|
if (!session)
|
|
1112
1222
|
return null;
|
|
1113
|
-
|
|
1114
|
-
if ((0, fs_1.existsSync)(sidecarPath)) {
|
|
1115
|
-
const sidecar = parseHarnessSidecarFile(sidecarPath);
|
|
1116
|
-
if (sidecar?.state) {
|
|
1117
|
-
return (0, harness_1.upgradeHarnessState)(sidecar.state, {
|
|
1118
|
-
cwd: session.cwd ?? session.projectPath,
|
|
1119
|
-
messages: readSessionMessages(sessionId),
|
|
1120
|
-
});
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
if (session.harnessState) {
|
|
1124
|
-
return (0, harness_1.upgradeHarnessState)(session.harnessState, {
|
|
1125
|
-
cwd: session.cwd ?? session.projectPath,
|
|
1126
|
-
messages: readSessionMessages(sessionId),
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
const messages = readSessionMessages(sessionId);
|
|
1130
|
-
if (messages.length > 0) {
|
|
1131
|
-
return (0, harness_1.upgradeHarnessState)(null, {
|
|
1132
|
-
cwd: session.cwd ?? session.projectPath,
|
|
1133
|
-
messages,
|
|
1134
|
-
});
|
|
1135
|
-
}
|
|
1136
|
-
return null;
|
|
1223
|
+
return restoreLegacyHarnessState(session, readSessionMessages(sessionId));
|
|
1137
1224
|
}
|
|
1138
1225
|
function updateSessionSkills(sessionId, skills) {
|
|
1139
1226
|
if (skills.length === 0)
|
|
@@ -1453,6 +1540,13 @@ function commitSessionCompactCheckpoint(input) {
|
|
|
1453
1540
|
return checkpoint;
|
|
1454
1541
|
}
|
|
1455
1542
|
function loadSessionTranscriptMessages(sessionId) {
|
|
1543
|
+
const rawSession = loadRawSessionMeta(sessionId);
|
|
1544
|
+
if (rawSession) {
|
|
1545
|
+
const threadSummary = (0, thread_session_view_1.loadThreadSessionSummaryV1)(rawSession.projectPath, rawSession.id);
|
|
1546
|
+
if (threadSummary) {
|
|
1547
|
+
return threadSummary.transcriptMessages.map(threadTranscriptMessageToSessionMessage);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1456
1550
|
return (withLockedSession(sessionId, session => {
|
|
1457
1551
|
const messages = readSessionMessagesForSession(session);
|
|
1458
1552
|
const checkpoint = loadCompactCheckpointForSession(session, messages);
|
|
@@ -1464,6 +1558,17 @@ function loadSessionTranscriptMessages(sessionId) {
|
|
|
1464
1558
|
: messages;
|
|
1465
1559
|
}) ?? []);
|
|
1466
1560
|
}
|
|
1561
|
+
function threadTranscriptMessageToSessionMessage(message) {
|
|
1562
|
+
return {
|
|
1563
|
+
role: message.role,
|
|
1564
|
+
content: message.content,
|
|
1565
|
+
timestamp: message.timestamp,
|
|
1566
|
+
...(message.modelVisibleContent ? { modelVisibleContent: message.modelVisibleContent } : {}),
|
|
1567
|
+
...(message.toolCallId ? { toolCallId: message.toolCallId } : {}),
|
|
1568
|
+
...(message.tool_calls ? { tool_calls: structuredClone(message.tool_calls) } : {}),
|
|
1569
|
+
...(message.appliedSkills ? { appliedSkills: [...message.appliedSkills] } : {}),
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1467
1572
|
function hasPersistedCompactContext(messages) {
|
|
1468
1573
|
return messages.some(message => message.content.includes('[Orion Code Context State v2]') ||
|
|
1469
1574
|
message.content.includes('[Context Summary]') ||
|
|
@@ -1552,6 +1657,7 @@ function updateSessionSummary(sessionId, _messages) {
|
|
|
1552
1657
|
session.filesModified = summary.filesModified;
|
|
1553
1658
|
session.taskSummary = summary.taskSummary;
|
|
1554
1659
|
session.messageCount = summary.messageCount;
|
|
1660
|
+
session.historySizeBytes = computeSessionHistorySizeBytes(session);
|
|
1555
1661
|
session.updatedAt = Date.now();
|
|
1556
1662
|
session.updatedAtIso = new Date(session.updatedAt).toISOString();
|
|
1557
1663
|
writeSessionMetaUnlocked(session);
|
|
@@ -1561,9 +1667,55 @@ function updateSessionSummary(sessionId, _messages) {
|
|
|
1561
1667
|
* 获取项目最近的会话
|
|
1562
1668
|
*/
|
|
1563
1669
|
function getLastSession(projectPath) {
|
|
1564
|
-
const sessions = listProjectSessions(projectPath)
|
|
1670
|
+
const sessions = filterSessionsWithRestorableHistory(listProjectSessions(projectPath));
|
|
1565
1671
|
return sessions[0] ?? null;
|
|
1566
1672
|
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Keep pre-read-model v2 Sessions discoverable without replaying their logs.
|
|
1675
|
+
* An indexed Thread with no bound read model is "unknown", not "empty"; its
|
|
1676
|
+
* metadata is repaired lazily when that target is actually opened.
|
|
1677
|
+
*/
|
|
1678
|
+
function filterSessionsWithRestorableHistory(sessions) {
|
|
1679
|
+
const threadSessionsByProject = new Map();
|
|
1680
|
+
const threadSessions = (projectPath) => {
|
|
1681
|
+
const cached = threadSessionsByProject.get(projectPath);
|
|
1682
|
+
if (cached)
|
|
1683
|
+
return cached;
|
|
1684
|
+
let entries = {};
|
|
1685
|
+
try {
|
|
1686
|
+
entries = (0, legacy_thread_materializer_1.loadThreadCutoverIndexV1)(projectPath).sessions;
|
|
1687
|
+
}
|
|
1688
|
+
catch (error) {
|
|
1689
|
+
(0, debug_log_1.debugError)('session-storage.restorableThreadIndex', error, projectPath);
|
|
1690
|
+
}
|
|
1691
|
+
threadSessionsByProject.set(projectPath, entries);
|
|
1692
|
+
return entries;
|
|
1693
|
+
};
|
|
1694
|
+
return sessions.filter(session => {
|
|
1695
|
+
if ((session.messageCount ?? 0) > 0)
|
|
1696
|
+
return true;
|
|
1697
|
+
const entry = threadSessions(session.projectPath)[session.id];
|
|
1698
|
+
if (!entry)
|
|
1699
|
+
return false;
|
|
1700
|
+
const readModel = session.threadReadModel;
|
|
1701
|
+
if (!readModel || readModel.threadId !== entry.threadId)
|
|
1702
|
+
return true;
|
|
1703
|
+
try {
|
|
1704
|
+
const stats = (0, fs_1.statSync)((0, path_1.join)((0, paths_1.getProjectThreadsV2Dir)(session.projectPath), `${readModel.threadId}.events.v1.jsonl`), { bigint: true });
|
|
1705
|
+
const headStillCurrent = stats.size === BigInt(session.historySizeBytes ?? -1) &&
|
|
1706
|
+
stats.dev.toString() === readModel.logDevice &&
|
|
1707
|
+
stats.ino.toString() === readModel.logInode &&
|
|
1708
|
+
stats.mtimeNs.toString() === readModel.logMtimeNs &&
|
|
1709
|
+
stats.ctimeNs.toString() === readModel.logCtimeNs;
|
|
1710
|
+
return !headStillCurrent;
|
|
1711
|
+
}
|
|
1712
|
+
catch {
|
|
1713
|
+
// Missing or unreadable authoritative facts must remain discoverable so
|
|
1714
|
+
// an explicit restore can surface the isolated corruption diagnostic.
|
|
1715
|
+
return true;
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1567
1719
|
// ============================================================================
|
|
1568
1720
|
// 历史记录 (JSONL)
|
|
1569
1721
|
// ============================================================================
|
|
@@ -1639,6 +1791,7 @@ function appendSessionMessage(sessionId, message) {
|
|
|
1639
1791
|
session.updatedAt = Date.now();
|
|
1640
1792
|
session.updatedAtIso = new Date(session.updatedAt).toISOString();
|
|
1641
1793
|
session.messageCount = (session.messageCount ?? 0) + 1;
|
|
1794
|
+
session.historySizeBytes = computeSessionHistorySizeBytes(session);
|
|
1642
1795
|
writeSessionMetaUnlocked(session);
|
|
1643
1796
|
});
|
|
1644
1797
|
}
|
|
@@ -1662,6 +1815,7 @@ function appendSessionMessages(sessionId, messages) {
|
|
|
1662
1815
|
session.updatedAt = Date.now();
|
|
1663
1816
|
session.updatedAtIso = new Date(session.updatedAt).toISOString();
|
|
1664
1817
|
session.messageCount = (session.messageCount ?? 0) + messages.length;
|
|
1818
|
+
session.historySizeBytes = computeSessionHistorySizeBytes(session);
|
|
1665
1819
|
writeSessionMetaUnlocked(session);
|
|
1666
1820
|
});
|
|
1667
1821
|
}
|
|
@@ -1695,6 +1849,7 @@ function overwriteSessionMessagesUnlocked(session, messages) {
|
|
|
1695
1849
|
(0, session_index_1.updateSessionIndex)(session.id, session.projectPath, message);
|
|
1696
1850
|
}
|
|
1697
1851
|
session.messageCount = messages.length;
|
|
1852
|
+
session.historySizeBytes = computeSessionHistorySizeBytes(session);
|
|
1698
1853
|
const summary = summarizeSessionMessages(messages);
|
|
1699
1854
|
session.toolsUsed = summary.toolsUsed;
|
|
1700
1855
|
session.filesModified = summary.filesModified;
|
|
@@ -1748,7 +1903,7 @@ function removeTrailingSessionUserMessage(sessionId) {
|
|
|
1748
1903
|
* 读取会话消息
|
|
1749
1904
|
*/
|
|
1750
1905
|
function readSessionMessages(sessionId) {
|
|
1751
|
-
const session =
|
|
1906
|
+
const session = loadRawSessionMeta(sessionId);
|
|
1752
1907
|
if (!session)
|
|
1753
1908
|
return [];
|
|
1754
1909
|
return readSessionMessagesForSession(session);
|
|
@@ -1846,37 +2001,144 @@ function readSessionTraceEvents(sessionId) {
|
|
|
1846
2001
|
* 包含完整的 tool_calls 信息,确保 LLM 能理解之前的工具调用
|
|
1847
2002
|
*/
|
|
1848
2003
|
function loadSessionHistory(sessionId) {
|
|
2004
|
+
return loadSessionHistoryWithDiagnostics(sessionId).messages.map(message => structuredClone(message));
|
|
2005
|
+
}
|
|
2006
|
+
/** Load provider-safe history together with any explicit recovery provenance. */
|
|
2007
|
+
function loadSessionHistoryWithDiagnostics(sessionId) {
|
|
2008
|
+
return loadSessionRestoreBundleInternal(sessionId, false).history;
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Capture every projection needed by /resume from one stable storage view.
|
|
2012
|
+
* A v2 target opens and verifies one ThreadEventStore; a legacy target reads
|
|
2013
|
+
* messages and its compact checkpoint under one Session lock.
|
|
2014
|
+
*/
|
|
2015
|
+
function loadSessionRestoreBundle(sessionId) {
|
|
2016
|
+
return loadSessionRestoreBundleInternal(sessionId, true);
|
|
2017
|
+
}
|
|
2018
|
+
function loadSessionRestoreBundleInternal(sessionId, includeRuntimeActivation) {
|
|
2019
|
+
const rawSession = loadRawSessionMeta(sessionId);
|
|
2020
|
+
if (rawSession) {
|
|
2021
|
+
const openedThread = includeRuntimeActivation
|
|
2022
|
+
? (0, thread_session_view_1.openThreadSessionViewV1)(rawSession.projectPath, rawSession.id)
|
|
2023
|
+
: undefined;
|
|
2024
|
+
const threadView = openedThread?.view ?? (0, thread_session_view_1.loadThreadSessionViewV1)(rawSession.projectPath, rawSession.id);
|
|
2025
|
+
if (threadView) {
|
|
2026
|
+
const harnessState = restoreThreadHarnessState(rawSession, threadView);
|
|
2027
|
+
try {
|
|
2028
|
+
updateSessionThreadReadModel(sessionId, {
|
|
2029
|
+
threadId: threadView.threadId,
|
|
2030
|
+
cursor: threadView.cursor,
|
|
2031
|
+
projectionDigest: threadView.projectionDigest,
|
|
2032
|
+
cutoverGeneration: threadView.readModel.cutoverGeneration,
|
|
2033
|
+
lastRecordHash: threadView.readModel.lastRecordHash,
|
|
2034
|
+
logDevice: threadView.readModel.log.device,
|
|
2035
|
+
logInode: threadView.readModel.log.inode,
|
|
2036
|
+
logMtimeNs: threadView.readModel.log.mtimeNs,
|
|
2037
|
+
logCtimeNs: threadView.readModel.log.ctimeNs,
|
|
2038
|
+
messageCount: threadView.messageCount,
|
|
2039
|
+
historySizeBytes: threadView.historySizeBytes,
|
|
2040
|
+
updatedAt: threadView.updatedAt,
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
catch (error) {
|
|
2044
|
+
(0, debug_log_1.debugError)('session-storage.repairThreadReadModel', error, sessionId);
|
|
2045
|
+
}
|
|
2046
|
+
return {
|
|
2047
|
+
history: {
|
|
2048
|
+
messages: threadView.modelHistory.map(message => structuredClone(message)),
|
|
2049
|
+
source: threadView.modelHistorySource,
|
|
2050
|
+
diagnostics: threadView.diagnostics,
|
|
2051
|
+
},
|
|
2052
|
+
transcriptMessages: threadView.transcriptMessages.map(threadTranscriptMessageToSessionMessage),
|
|
2053
|
+
sourceMessageCount: threadView.transcriptMessages.length,
|
|
2054
|
+
checkpoint: null,
|
|
2055
|
+
...(harnessState ? { harnessState } : {}),
|
|
2056
|
+
...(openedThread ? { runtimeActivation: openedThread.runtimeActivation } : {}),
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
1849
2060
|
return (withLockedSession(sessionId, session => {
|
|
1850
2061
|
const messages = readSessionMessagesForSession(session);
|
|
2062
|
+
const harnessState = restoreLegacyHarnessState(session, messages);
|
|
1851
2063
|
const checkpoint = loadCompactCheckpointForSession(session, messages);
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
];
|
|
1857
|
-
if (checkpoint.version === 2) {
|
|
1858
|
-
try {
|
|
1859
|
-
(0, tool_call_groups_1.assertToolCallGroups)(restored);
|
|
1860
|
-
return restored;
|
|
1861
|
-
}
|
|
1862
|
-
catch (error) {
|
|
1863
|
-
// Never mutate or synthesize data inside a validated V2
|
|
1864
|
-
// replacement. Fall back to the append-only transcript instead.
|
|
1865
|
-
(0, debug_log_1.debugError)('session-storage.restoreCompactCheckpoint.toolGroups', error, sessionId);
|
|
1866
|
-
return (0, tool_call_groups_1.sealToolCallGroups)(messages.map(sessionMessageToModelMessage));
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
return (0, tool_call_groups_1.sealToolCallGroups)(restored);
|
|
1870
|
-
}
|
|
1871
|
-
let modelVisibleMessages = messages;
|
|
1872
|
-
if (typeof session.transcriptDisplayStartTime === 'number') {
|
|
1873
|
-
const afterDisplayStart = messages.filter(message => (message.timestamp ?? 0) >= (session.transcriptDisplayStartTime ?? 0));
|
|
1874
|
-
modelVisibleMessages = hasPersistedCompactContext(afterDisplayStart)
|
|
1875
|
-
? afterDisplayStart
|
|
2064
|
+
const transcriptMessages = checkpoint
|
|
2065
|
+
? messages.slice(checkpoint.transcriptStartMessageIndex)
|
|
2066
|
+
: typeof session.transcriptDisplayStartTime === 'number'
|
|
2067
|
+
? messages.filter(message => (message.timestamp ?? 0) >= (session.transcriptDisplayStartTime ?? 0))
|
|
1876
2068
|
: messages;
|
|
2069
|
+
return {
|
|
2070
|
+
history: {
|
|
2071
|
+
messages: restoreLegacyModelHistory(sessionId, session, messages, checkpoint),
|
|
2072
|
+
source: 'legacy',
|
|
2073
|
+
diagnostics: [],
|
|
2074
|
+
},
|
|
2075
|
+
transcriptMessages,
|
|
2076
|
+
sourceMessageCount: messages.length,
|
|
2077
|
+
checkpoint,
|
|
2078
|
+
...(harnessState ? { harnessState } : {}),
|
|
2079
|
+
};
|
|
2080
|
+
}) ?? {
|
|
2081
|
+
history: { messages: [], source: 'legacy', diagnostics: [] },
|
|
2082
|
+
transcriptMessages: [],
|
|
2083
|
+
sourceMessageCount: 0,
|
|
2084
|
+
checkpoint: null,
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
function restoreThreadHarnessState(session, view) {
|
|
2088
|
+
if (!view.latestTurnCommit) {
|
|
2089
|
+
return restoreLegacyHarnessState(session, view.transcriptMessages.map(threadTranscriptMessageToSessionMessage));
|
|
2090
|
+
}
|
|
2091
|
+
const commit = (0, turn_commit_1.parseTurnCommitV1)(view.latestTurnCommit.receipt);
|
|
2092
|
+
let state;
|
|
2093
|
+
try {
|
|
2094
|
+
state = JSON.parse(commit.taskContext);
|
|
2095
|
+
}
|
|
2096
|
+
catch {
|
|
2097
|
+
throw new Error(`Session ${session.id} TurnCommit TaskContext is not valid JSON.`);
|
|
2098
|
+
}
|
|
2099
|
+
return (0, harness_1.upgradeHarnessState)(state, {
|
|
2100
|
+
cwd: session.cwd ?? session.projectPath,
|
|
2101
|
+
messages: view.modelHistory.map(message => structuredClone(message)),
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
function restoreLegacyHarnessState(session, messages) {
|
|
2105
|
+
const sidecarPath = (0, config_dir_1.getProjectSessionHarnessPath)(session.projectPath, session.id);
|
|
2106
|
+
const sidecar = (0, fs_1.existsSync)(sidecarPath) ? parseHarnessSidecarFile(sidecarPath) : null;
|
|
2107
|
+
const state = sidecar?.state ?? session.harnessState ?? null;
|
|
2108
|
+
if (!state && messages.length === 0)
|
|
2109
|
+
return null;
|
|
2110
|
+
return (0, harness_1.upgradeHarnessState)(state, {
|
|
2111
|
+
cwd: session.cwd ?? session.projectPath,
|
|
2112
|
+
messages: messages.map(message => structuredClone(message)),
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
function restoreLegacyModelHistory(sessionId, session, messages, checkpoint) {
|
|
2116
|
+
if (checkpoint) {
|
|
2117
|
+
const restored = [
|
|
2118
|
+
...checkpoint.modelHistory.map(message => ({ ...message })),
|
|
2119
|
+
...messages.slice(checkpoint.sourceMessageCount).map(sessionMessageToModelMessage),
|
|
2120
|
+
];
|
|
2121
|
+
if (checkpoint.version === 2) {
|
|
2122
|
+
try {
|
|
2123
|
+
(0, tool_call_groups_1.assertToolCallGroups)(restored);
|
|
2124
|
+
return restored;
|
|
2125
|
+
}
|
|
2126
|
+
catch (error) {
|
|
2127
|
+
// Never mutate or synthesize data inside a validated V2 replacement.
|
|
2128
|
+
(0, debug_log_1.debugError)('session-storage.restoreCompactCheckpoint.toolGroups', error, sessionId);
|
|
2129
|
+
return (0, tool_call_groups_1.sealToolCallGroups)(messages.map(sessionMessageToModelMessage));
|
|
2130
|
+
}
|
|
1877
2131
|
}
|
|
1878
|
-
return (0, tool_call_groups_1.sealToolCallGroups)(
|
|
1879
|
-
}
|
|
2132
|
+
return (0, tool_call_groups_1.sealToolCallGroups)(restored);
|
|
2133
|
+
}
|
|
2134
|
+
let modelVisibleMessages = messages;
|
|
2135
|
+
if (typeof session.transcriptDisplayStartTime === 'number') {
|
|
2136
|
+
const afterDisplayStart = messages.filter(message => (message.timestamp ?? 0) >= (session.transcriptDisplayStartTime ?? 0));
|
|
2137
|
+
modelVisibleMessages = hasPersistedCompactContext(afterDisplayStart)
|
|
2138
|
+
? afterDisplayStart
|
|
2139
|
+
: messages;
|
|
2140
|
+
}
|
|
2141
|
+
return (0, tool_call_groups_1.sealToolCallGroups)(modelVisibleMessages.map(sessionMessageToModelMessage));
|
|
1880
2142
|
}
|
|
1881
2143
|
function sessionMessageToModelMessage(message) {
|
|
1882
2144
|
const result = {
|
|
@@ -1919,10 +2181,14 @@ function findSession(ref, projectPath, options = {}) {
|
|
|
1919
2181
|
* conflict prompts.
|
|
1920
2182
|
*/
|
|
1921
2183
|
function lookupSessionRef(ref, projectPath, options = {}) {
|
|
2184
|
+
const candidates = options.allProjects || !projectPath ? listSessions() : listProjectSessions(projectPath);
|
|
2185
|
+
return lookupSessionRefInSessions(ref, candidates);
|
|
2186
|
+
}
|
|
2187
|
+
/** Resolve a reference against an already-loaded picker snapshot. */
|
|
2188
|
+
function lookupSessionRefInSessions(ref, candidates) {
|
|
1922
2189
|
const query = ref.trim();
|
|
1923
2190
|
if (!query)
|
|
1924
2191
|
return { status: 'not_found' };
|
|
1925
|
-
const candidates = options.allProjects || !projectPath ? listSessions() : listProjectSessions(projectPath);
|
|
1926
2192
|
const exactId = candidates.find(session => session.id === query);
|
|
1927
2193
|
if (exactId)
|
|
1928
2194
|
return { status: 'found', session: exactId };
|