poly-weaver 0.15.2 → 1.0.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/dist/agents/implementors/handler.d.ts.map +1 -1
- package/dist/agents/implementors/handler.js +7 -2
- package/dist/agents/implementors/handler.js.map +1 -1
- package/dist/agents/interviewers/handler.d.ts.map +1 -1
- package/dist/agents/interviewers/handler.js +3 -1
- package/dist/agents/interviewers/handler.js.map +1 -1
- package/dist/agents/planners/adapter.d.ts.map +1 -1
- package/dist/agents/planners/adapter.js +30 -11
- package/dist/agents/planners/adapter.js.map +1 -1
- package/dist/agents/planners/handler.d.ts.map +1 -1
- package/dist/agents/planners/handler.js +7 -2
- package/dist/agents/planners/handler.js.map +1 -1
- package/dist/agents/reviewers/adapter.d.ts.map +1 -1
- package/dist/agents/reviewers/adapter.js +1 -0
- package/dist/agents/reviewers/adapter.js.map +1 -1
- package/dist/agents/reviewers/handler.d.ts.map +1 -1
- package/dist/agents/reviewers/handler.js +23 -4
- package/dist/agents/reviewers/handler.js.map +1 -1
- package/dist/agents/reviewers/types.d.ts +2 -0
- package/dist/agents/reviewers/types.d.ts.map +1 -1
- package/dist/agents/runner.d.ts +18 -1
- package/dist/agents/runner.d.ts.map +1 -1
- package/dist/agents/runner.js +136 -20
- package/dist/agents/runner.js.map +1 -1
- package/dist/agents/simplifiers/handler.d.ts.map +1 -1
- package/dist/agents/simplifiers/handler.js +7 -2
- package/dist/agents/simplifiers/handler.js.map +1 -1
- package/dist/auto-update/detect-agents.d.ts +1 -0
- package/dist/auto-update/detect-agents.d.ts.map +1 -1
- package/dist/auto-update/detect-agents.js +8 -7
- package/dist/auto-update/detect-agents.js.map +1 -1
- package/dist/cli.js +16 -4
- package/dist/cli.js.map +1 -1
- package/dist/crosscheck/coordinator.d.ts +5 -0
- package/dist/crosscheck/coordinator.d.ts.map +1 -1
- package/dist/crosscheck/coordinator.js +216 -96
- package/dist/crosscheck/coordinator.js.map +1 -1
- package/dist/crosscheck/live-agent-session.d.ts +10 -1
- package/dist/crosscheck/live-agent-session.d.ts.map +1 -1
- package/dist/crosscheck/live-agent-session.js +68 -7
- package/dist/crosscheck/live-agent-session.js.map +1 -1
- package/dist/crosscheck/state.d.ts +3 -0
- package/dist/crosscheck/state.d.ts.map +1 -1
- package/dist/crosscheck/state.js +17 -2
- package/dist/crosscheck/state.js.map +1 -1
- package/dist/dump/collector.d.ts.map +1 -1
- package/dist/dump/collector.js +3 -0
- package/dist/dump/collector.js.map +1 -1
- package/dist/dump/service.d.ts.map +1 -1
- package/dist/dump/service.js +3 -0
- package/dist/dump/service.js.map +1 -1
- package/dist/dump/types.d.ts +3 -0
- package/dist/dump/types.d.ts.map +1 -1
- package/dist/dump/types.js.map +1 -1
- package/dist/flow/executor.d.ts.map +1 -1
- package/dist/flow/executor.js +5 -0
- package/dist/flow/executor.js.map +1 -1
- package/dist/flow/types.d.ts +5 -0
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/flow/types.js.map +1 -1
- package/dist/offscreen/controller.d.ts +19 -5
- package/dist/offscreen/controller.d.ts.map +1 -1
- package/dist/offscreen/controller.js +336 -159
- package/dist/offscreen/controller.js.map +1 -1
- package/dist/offscreen/fork-launch.d.ts +2 -2
- package/dist/offscreen/fork-launch.d.ts.map +1 -1
- package/dist/offscreen/fork-launch.js +2 -2
- package/dist/offscreen/fork-launch.js.map +1 -1
- package/dist/offscreen/fork.d.ts +16 -2
- package/dist/offscreen/fork.d.ts.map +1 -1
- package/dist/offscreen/fork.js +79 -40
- package/dist/offscreen/fork.js.map +1 -1
- package/dist/offscreen/inquiry-pane.d.ts +2 -1
- package/dist/offscreen/inquiry-pane.d.ts.map +1 -1
- package/dist/offscreen/inquiry-pane.js +8 -2
- package/dist/offscreen/inquiry-pane.js.map +1 -1
- package/dist/offscreen/inquiry-sender.d.ts +1 -0
- package/dist/offscreen/inquiry-sender.d.ts.map +1 -1
- package/dist/offscreen/inquiry-sender.js +1 -0
- package/dist/offscreen/inquiry-sender.js.map +1 -1
- package/dist/offscreen/panes.d.ts +1 -0
- package/dist/offscreen/panes.d.ts.map +1 -1
- package/dist/offscreen/registry.d.ts +2 -2
- package/dist/offscreen/registry.d.ts.map +1 -1
- package/dist/offscreen/registry.js +5 -1
- package/dist/offscreen/registry.js.map +1 -1
- package/dist/offscreen/resolve.d.ts +2 -0
- package/dist/offscreen/resolve.d.ts.map +1 -1
- package/dist/offscreen/resolve.js +5 -1
- package/dist/offscreen/resolve.js.map +1 -1
- package/dist/offscreen/session-turn-monitor.d.ts +34 -33
- package/dist/offscreen/session-turn-monitor.d.ts.map +1 -1
- package/dist/offscreen/session-turn-monitor.js +327 -281
- package/dist/offscreen/session-turn-monitor.js.map +1 -1
- package/dist/offscreen/session-turn-receipt.d.ts +28 -0
- package/dist/offscreen/session-turn-receipt.d.ts.map +1 -0
- package/dist/offscreen/session-turn-receipt.js +53 -0
- package/dist/offscreen/session-turn-receipt.js.map +1 -0
- package/dist/offscreen/split-inquiry.d.ts.map +1 -1
- package/dist/offscreen/split-inquiry.js +5 -0
- package/dist/offscreen/split-inquiry.js.map +1 -1
- package/dist/offscreen/starting-pane.d.ts +13 -0
- package/dist/offscreen/starting-pane.d.ts.map +1 -0
- package/dist/offscreen/starting-pane.js +24 -0
- package/dist/offscreen/starting-pane.js.map +1 -0
- package/dist/offscreen/surface.d.ts +1 -0
- package/dist/offscreen/surface.d.ts.map +1 -1
- package/dist/offscreen/surface.js +5 -3
- package/dist/offscreen/surface.js.map +1 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +7 -1
- package/dist/orchestrator.js.map +1 -1
- package/dist/providers/base-completion.d.ts +1 -0
- package/dist/providers/base-completion.d.ts.map +1 -1
- package/dist/providers/base-completion.js +4 -2
- package/dist/providers/base-completion.js.map +1 -1
- package/dist/providers/codex/completion.d.ts +4 -2
- package/dist/providers/codex/completion.d.ts.map +1 -1
- package/dist/providers/codex/completion.js +99 -14
- package/dist/providers/codex/completion.js.map +1 -1
- package/dist/providers/codex/strategy.d.ts.map +1 -1
- package/dist/providers/codex/strategy.js +3 -2
- package/dist/providers/codex/strategy.js.map +1 -1
- package/dist/providers/copilot/completion.d.ts +32 -21
- package/dist/providers/copilot/completion.d.ts.map +1 -1
- package/dist/providers/copilot/completion.js +110 -166
- package/dist/providers/copilot/completion.js.map +1 -1
- package/dist/providers/copilot/environment.d.ts +2 -0
- package/dist/providers/copilot/environment.d.ts.map +1 -0
- package/dist/providers/copilot/environment.js +8 -0
- package/dist/providers/copilot/environment.js.map +1 -0
- package/dist/providers/copilot/fork-state.d.ts +7 -0
- package/dist/providers/copilot/fork-state.d.ts.map +1 -0
- package/dist/providers/copilot/fork-state.js +150 -0
- package/dist/providers/copilot/fork-state.js.map +1 -0
- package/dist/providers/copilot/observation.d.ts +275 -0
- package/dist/providers/copilot/observation.d.ts.map +1 -0
- package/dist/providers/copilot/observation.js +2072 -0
- package/dist/providers/copilot/observation.js.map +1 -0
- package/dist/providers/copilot/observer-extension/com.github.copilot/extensions/poly-weaver-observer/extension.d.mts +2 -0
- package/dist/providers/copilot/observer-extension/com.github.copilot/extensions/poly-weaver-observer/extension.d.mts.map +1 -0
- package/dist/providers/copilot/observer-extension/com.github.copilot/extensions/poly-weaver-observer/extension.mjs +426 -0
- package/dist/providers/copilot/observer-extension/com.github.copilot/extensions/poly-weaver-observer/extension.mjs.map +1 -0
- package/dist/providers/copilot/observer-extension/plugin.json +6 -0
- package/dist/providers/copilot/record-properties.d.ts +4 -0
- package/dist/providers/copilot/record-properties.d.ts.map +1 -0
- package/dist/providers/copilot/record-properties.js +14 -0
- package/dist/providers/copilot/record-properties.js.map +1 -0
- package/dist/providers/copilot/session.d.ts +8 -33
- package/dist/providers/copilot/session.d.ts.map +1 -1
- package/dist/providers/copilot/session.js +10 -176
- package/dist/providers/copilot/session.js.map +1 -1
- package/dist/providers/copilot/strategy.d.ts +4 -5
- package/dist/providers/copilot/strategy.d.ts.map +1 -1
- package/dist/providers/copilot/strategy.js +73 -67
- package/dist/providers/copilot/strategy.js.map +1 -1
- package/dist/providers/fork-cleanup.d.ts +4 -0
- package/dist/providers/fork-cleanup.d.ts.map +1 -0
- package/dist/providers/fork-cleanup.js +21 -0
- package/dist/providers/fork-cleanup.js.map +1 -0
- package/dist/providers/fork.d.ts +24 -3
- package/dist/providers/fork.d.ts.map +1 -1
- package/dist/providers/fork.js.map +1 -1
- package/dist/providers/invoke.d.ts.map +1 -1
- package/dist/providers/invoke.js +85 -34
- package/dist/providers/invoke.js.map +1 -1
- package/dist/providers/live.d.ts.map +1 -1
- package/dist/providers/live.js +62 -17
- package/dist/providers/live.js.map +1 -1
- package/dist/providers/observer-run.d.ts +4 -0
- package/dist/providers/observer-run.d.ts.map +1 -0
- package/dist/providers/observer-run.js +23 -0
- package/dist/providers/observer-run.js.map +1 -0
- package/dist/providers/session-launch.d.ts +5 -1
- package/dist/providers/session-launch.d.ts.map +1 -1
- package/dist/providers/session-launch.js +42 -16
- package/dist/providers/session-launch.js.map +1 -1
- package/dist/providers/types.d.ts +156 -4
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/session/resume.d.ts.map +1 -1
- package/dist/session/resume.js +16 -2
- package/dist/session/resume.js.map +1 -1
- package/dist/terminal/paste-submit.d.ts +1 -0
- package/dist/terminal/paste-submit.d.ts.map +1 -1
- package/dist/terminal/paste-submit.js +45 -32
- package/dist/terminal/paste-submit.js.map +1 -1
- package/dist/user/host-curate-prompt.d.ts.map +1 -1
- package/dist/user/host-curate-prompt.js +9 -6
- package/dist/user/host-curate-prompt.js.map +1 -1
- package/dist/user/host-prompt.d.ts.map +1 -1
- package/dist/user/host-prompt.js +9 -6
- package/dist/user/host-prompt.js.map +1 -1
- package/package.json +4 -2
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { resolveProvider } from "../providers/registry.js";
|
|
2
2
|
import { readSessionFileWithStatus, } from "../session/reader.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
3
|
+
import { SessionTurnReceipt } from "./session-turn-receipt.js";
|
|
4
|
+
export { SessionTurnReceipt } from "./session-turn-receipt.js";
|
|
5
|
+
export const DEFAULT_TURN_ATTEMPT_TIMEOUT_MS = 5_000;
|
|
6
|
+
/** Ordered request ownership; provider observations remain work even without a host receipt. */
|
|
8
7
|
export class SessionTurnMonitor {
|
|
9
8
|
driver;
|
|
10
9
|
sessionFilePath;
|
|
@@ -12,136 +11,174 @@ export class SessionTurnMonitor {
|
|
|
12
11
|
baseline;
|
|
13
12
|
busy = false;
|
|
14
13
|
timer;
|
|
15
|
-
polling = false;
|
|
16
14
|
activePoll;
|
|
17
15
|
stopped = false;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
pendingTurnAttempts = [];
|
|
24
|
-
unreservedObservedGenerations = [];
|
|
25
|
-
pendingAttemptWaiters = new Set();
|
|
16
|
+
attachingBatch = false;
|
|
17
|
+
ownershipWarned = false;
|
|
18
|
+
entries = [];
|
|
19
|
+
attached = new Set();
|
|
20
|
+
attemptWaiters = new Set();
|
|
26
21
|
observedSubmitCount;
|
|
27
22
|
pollMs;
|
|
28
23
|
attemptTimeoutMs;
|
|
29
24
|
submitFloor;
|
|
30
25
|
historySnapshot;
|
|
31
|
-
mappedSubmitIdentities = [];
|
|
32
26
|
constructor(driver, sessionFilePath, baseline, options = {}) {
|
|
33
27
|
this.driver = driver;
|
|
34
28
|
this.sessionFilePath = sessionFilePath;
|
|
35
29
|
this.options = options;
|
|
36
30
|
this.baseline = Math.max(0, baseline);
|
|
37
31
|
this.pollMs = options.pollMs ?? 500;
|
|
38
|
-
this.attemptTimeoutMs =
|
|
39
|
-
options.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;
|
|
32
|
+
this.attemptTimeoutMs = options.attemptTimeoutMs ?? DEFAULT_TURN_ATTEMPT_TIMEOUT_MS;
|
|
40
33
|
this.submitFloor = options.submitFloor;
|
|
41
34
|
this.historySnapshot = options.initialHistorySnapshot;
|
|
42
35
|
}
|
|
43
|
-
get isIdle() {
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
get
|
|
47
|
-
return this.
|
|
48
|
-
|
|
49
|
-
get currentSubmitFloor() {
|
|
50
|
-
return this.submitFloor;
|
|
36
|
+
get isIdle() { return !this.busy; }
|
|
37
|
+
get lineBaseline() { return this.baseline; }
|
|
38
|
+
get currentSubmitFloor() { return this.submitFloor; }
|
|
39
|
+
get hasUnobservedExpiredAttempts() {
|
|
40
|
+
return this.entries.some((entry) => entry.ordinal === undefined
|
|
41
|
+
&& entry.receipt?.settlement === "expired");
|
|
51
42
|
}
|
|
52
43
|
get supportsHistoryRegression() {
|
|
53
|
-
|
|
44
|
+
const provider = resolveProvider(this.driver);
|
|
45
|
+
return provider.forkSubmitFloor !== undefined
|
|
46
|
+
&& provider.fork.sessionHistorySnapshot !== undefined;
|
|
54
47
|
}
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
reserveTurn(boundary = {}) {
|
|
49
|
+
const receipt = new SessionTurnReceipt({
|
|
50
|
+
...boundary,
|
|
51
|
+
anchorSubmitCount: boundary.anchorSubmitCount ?? this.observedSubmitCount ?? this.submitFloor,
|
|
52
|
+
});
|
|
53
|
+
this.attachTurn(receipt);
|
|
54
|
+
return receipt;
|
|
61
55
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this.ensurePolling();
|
|
56
|
+
async reserveTurnFromSession() {
|
|
57
|
+
await this.expectedSubmitCountFromSession();
|
|
58
|
+
return this.reserveTurn();
|
|
66
59
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
/** Attach the same pre-input receipt after discovery, never an anonymous later registration. */
|
|
61
|
+
attachTurns(receipts, initialLines) {
|
|
62
|
+
this.attachingBatch = true;
|
|
63
|
+
try {
|
|
64
|
+
for (const receipt of [...receipts].sort((a, b) => a.sequence - b.sequence))
|
|
65
|
+
this.attachTurn(receipt);
|
|
66
|
+
if (initialLines)
|
|
67
|
+
this.expectedSubmitCount(initialLines);
|
|
70
68
|
}
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
-
}
|
|
69
|
+
finally {
|
|
70
|
+
this.attachingBatch = false;
|
|
71
|
+
}
|
|
72
|
+
this.updateActivity(true);
|
|
73
|
+
if (this.busy || this.hasUnobservedExpiredAttempts)
|
|
74
|
+
this.ensurePolling();
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
-
if (this.stopped)
|
|
76
|
+
attachTurn(receipt) {
|
|
77
|
+
if (this.stopped || this.attached.has(receipt.id))
|
|
77
78
|
return;
|
|
78
|
-
|
|
79
|
+
this.attached.add(receipt.id);
|
|
80
|
+
if (receipt.settlement && receipt.settlement !== "expired")
|
|
79
81
|
return;
|
|
80
|
-
this.
|
|
81
|
-
this.
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const anchor = receipt.anchorSubmitCount ?? this.observedSubmitCount ?? this.submitFloor;
|
|
83
|
+
const observed = anchor === undefined ? undefined : this.entries.find((entry) => !entry.receipt && entry.ordinal !== undefined && entry.ordinal > anchor);
|
|
84
|
+
const entry = observed ?? { anchor };
|
|
85
|
+
entry.receipt = receipt;
|
|
86
|
+
if (!observed)
|
|
87
|
+
this.entries.push(entry);
|
|
88
|
+
entry.unsubscribe = receipt.onChange(() => {
|
|
89
|
+
if (this.stopped)
|
|
90
|
+
return;
|
|
91
|
+
if (receipt.settlement === "released" && entry.ordinal === undefined) {
|
|
92
|
+
this.removeEntry(entry);
|
|
93
|
+
this.updateActivity();
|
|
94
|
+
}
|
|
84
95
|
});
|
|
85
|
-
if (!
|
|
86
|
-
this.
|
|
87
|
-
|
|
96
|
+
if (receipt.rawAttempt && !receipt.settlement && entry.ordinal === undefined) {
|
|
97
|
+
const remainingMs = receipt.startedAt + this.attemptTimeoutMs - Date.now();
|
|
98
|
+
if (remainingMs <= 0) {
|
|
99
|
+
receipt.settle("expired");
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
entry.timer = setTimeout(() => {
|
|
103
|
+
if (this.stopped || entry.ordinal !== undefined)
|
|
104
|
+
return;
|
|
105
|
+
entry.timer = undefined;
|
|
106
|
+
// Expiry releases speculative busy state, not its place ahead of later input.
|
|
107
|
+
receipt.settle("expired");
|
|
108
|
+
this.updateActivity();
|
|
109
|
+
}, remainingMs);
|
|
110
|
+
}
|
|
88
111
|
}
|
|
89
|
-
this.
|
|
112
|
+
this.updateActivity(receipt.settlement === "expired");
|
|
113
|
+
if (entry.settlement)
|
|
114
|
+
receipt.settle(entry.settlement);
|
|
115
|
+
if (!this.attachingBatch && (this.busy || this.hasUnobservedExpiredAttempts))
|
|
116
|
+
this.ensurePolling();
|
|
117
|
+
}
|
|
118
|
+
beginTurn(receipt = this.reserveTurn()) {
|
|
119
|
+
this.attachTurn(receipt);
|
|
120
|
+
receipt.confirm();
|
|
121
|
+
return receipt;
|
|
90
122
|
}
|
|
91
|
-
beginTurnAttempt(
|
|
123
|
+
beginTurnAttempt(boundary = {}) {
|
|
124
|
+
const receipt = new SessionTurnReceipt({
|
|
125
|
+
...boundary,
|
|
126
|
+
anchorSubmitCount: boundary.anchorSubmitCount ?? this.observedSubmitCount ?? this.submitFloor,
|
|
127
|
+
}, true);
|
|
128
|
+
receipt.markWritten();
|
|
129
|
+
this.attachTurn(receipt);
|
|
130
|
+
return receipt;
|
|
131
|
+
}
|
|
132
|
+
expectedSubmitCount(lines) {
|
|
92
133
|
if (this.stopped)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (this.stopped || !this.pendingTurnAttempts.includes(attempt))
|
|
111
|
-
return;
|
|
112
|
-
attempt.expired = true;
|
|
113
|
-
this.discardExpiredAttempts();
|
|
114
|
-
this.releaseIfSettled();
|
|
115
|
-
}, Math.max(0, startedAt + this.attemptTimeoutMs - Date.now()));
|
|
116
|
-
this.pendingTurnAttempts.push(attempt);
|
|
117
|
-
if (!this.busy) {
|
|
118
|
-
this.busy = true;
|
|
119
|
-
this.options.onChange?.(false);
|
|
120
|
-
}
|
|
121
|
-
this.ensurePolling();
|
|
134
|
+
throw new Error("Session turn monitor is shut down");
|
|
135
|
+
const evidence = this.validateEvidence(lines);
|
|
136
|
+
const snapshot = this.reconcileHistory(lines);
|
|
137
|
+
this.observeSubmissions(lines, snapshot, evidence);
|
|
138
|
+
if (!this.attachingBatch && (this.busy || this.hasUnobservedExpiredAttempts))
|
|
139
|
+
this.ensurePolling();
|
|
140
|
+
return (this.observedSubmitCount ?? 0)
|
|
141
|
+
+ this.entries.filter((entry) => entry.ordinal === undefined && !entry.receipt?.settlement).length;
|
|
142
|
+
}
|
|
143
|
+
startHistoryPolling() {
|
|
144
|
+
if (!this.stopped && this.supportsHistoryRegression)
|
|
145
|
+
this.ensurePolling();
|
|
146
|
+
}
|
|
147
|
+
waitForPendingTurnAttempts() {
|
|
148
|
+
if (!this.hasPendingAttempts() || this.stopped)
|
|
149
|
+
return Promise.resolve();
|
|
150
|
+
return new Promise((resolve) => this.attemptWaiters.add(resolve));
|
|
122
151
|
}
|
|
123
152
|
async refreshIdleBaseline() {
|
|
124
|
-
const result = await this.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
this.
|
|
153
|
+
const result = await this.readValidResult();
|
|
154
|
+
this.expectedSubmitCount(result.lines);
|
|
155
|
+
if (this.isIdle)
|
|
156
|
+
this.baseline = result.lines.length;
|
|
157
|
+
this.retireNonSubmittingAttempts(result);
|
|
129
158
|
}
|
|
130
159
|
async expectedSubmitCountFromSession() {
|
|
131
|
-
const result = await this.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return
|
|
160
|
+
const result = await this.readValidResult();
|
|
161
|
+
const count = this.expectedSubmitCount(result.lines);
|
|
162
|
+
this.retireNonSubmittingAttempts(result);
|
|
163
|
+
return count;
|
|
164
|
+
}
|
|
165
|
+
async refreshExpiredAttempts() {
|
|
166
|
+
if (!this.hasUnobservedExpiredAttempts)
|
|
167
|
+
return;
|
|
168
|
+
await this.activePoll;
|
|
169
|
+
if (this.hasUnobservedExpiredAttempts)
|
|
170
|
+
await this.pollNow();
|
|
135
171
|
}
|
|
136
172
|
dispose() {
|
|
137
173
|
if (this.timer)
|
|
138
174
|
clearInterval(this.timer);
|
|
139
175
|
this.timer = undefined;
|
|
140
|
-
for (const
|
|
141
|
-
if (
|
|
142
|
-
clearTimeout(
|
|
176
|
+
for (const entry of this.entries) {
|
|
177
|
+
if (entry.timer)
|
|
178
|
+
clearTimeout(entry.timer);
|
|
179
|
+
entry.unsubscribe?.();
|
|
143
180
|
}
|
|
144
|
-
this.
|
|
181
|
+
this.resolveAttemptWaiters();
|
|
145
182
|
}
|
|
146
183
|
async shutdown() {
|
|
147
184
|
this.stopped = true;
|
|
@@ -149,234 +186,245 @@ export class SessionTurnMonitor {
|
|
|
149
186
|
await this.activePoll;
|
|
150
187
|
}
|
|
151
188
|
pollNow() {
|
|
152
|
-
if (
|
|
189
|
+
if (this.jsonlRuntimeDisabled())
|
|
190
|
+
return Promise.resolve();
|
|
191
|
+
if ((!this.busy && !this.supportsHistoryRegression && !this.hasUnobservedExpiredAttempts)
|
|
192
|
+
|| this.stopped) {
|
|
153
193
|
return Promise.resolve();
|
|
154
194
|
}
|
|
155
|
-
if (this.
|
|
156
|
-
return this.activePoll
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
active = this.pollOnce()
|
|
160
|
-
.catch((err) => {
|
|
195
|
+
if (this.activePoll)
|
|
196
|
+
return this.activePoll;
|
|
197
|
+
const active = this.pollOnce()
|
|
198
|
+
.catch((error) => {
|
|
161
199
|
if (!this.stopped)
|
|
162
|
-
this.options.onReadFailure?.(
|
|
200
|
+
this.options.onReadFailure?.(error);
|
|
163
201
|
})
|
|
164
202
|
.finally(() => {
|
|
165
203
|
if (this.activePoll === active)
|
|
166
204
|
this.activePoll = undefined;
|
|
167
|
-
this.polling = false;
|
|
168
205
|
});
|
|
169
206
|
this.activePoll = active;
|
|
170
207
|
return active;
|
|
171
208
|
}
|
|
172
209
|
async pollOnce() {
|
|
173
|
-
const result = await this.
|
|
174
|
-
|
|
175
|
-
throw new Error("Session log contains malformed JSONL");
|
|
176
|
-
const lines = result.lines;
|
|
210
|
+
const result = await this.readValidResult();
|
|
211
|
+
const { lines } = result;
|
|
177
212
|
if (this.stopped)
|
|
178
213
|
return;
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this.discardExpiredAttempts();
|
|
185
|
-
const unsettledSubmits = currentSubmitCount - priorSubmitCount;
|
|
186
|
-
if (unsettledSubmits > 0) {
|
|
187
|
-
const tail = lines.slice(this.baseline);
|
|
188
|
-
const latestSubmit = provider.fork.latestSubmitIndex(tail);
|
|
189
|
-
if (latestSubmit >= 0
|
|
190
|
-
&& provider.fork.turnComplete(lines, this.baseline + latestSubmit)) {
|
|
191
|
-
this.submitFloor = currentSubmitCount;
|
|
192
|
-
this.coveredGeneration = Math.min(this.mappedGeneration, this.coveredGeneration + unsettledSubmits);
|
|
193
|
-
this.baseline = lines.length;
|
|
214
|
+
const evidence = this.validateEvidence(lines, this.options.pendingEvidencePolicy === "wait");
|
|
215
|
+
if (evidence?.indeterminate) {
|
|
216
|
+
const { prior, assignments } = this.prepareObservation(lines);
|
|
217
|
+
if (this.inspectHistory(lines).regression) {
|
|
218
|
+
throw new Error("Session submission history regressed during pending evidence");
|
|
194
219
|
}
|
|
220
|
+
// Check projected ownership without committing any part of an incomplete observation.
|
|
221
|
+
this.warnAmbiguousOwnership(this.entries.map((entry) => ({
|
|
222
|
+
...entry,
|
|
223
|
+
anchor: entry.anchor ?? prior,
|
|
224
|
+
ordinal: entry.ordinal ?? assignments.get(entry),
|
|
225
|
+
})));
|
|
226
|
+
return;
|
|
195
227
|
}
|
|
196
|
-
this.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
for (const turn of this.pendingConfirmedTurns) {
|
|
206
|
-
turn.anchorSubmitCount ??= observedSubmitCount;
|
|
228
|
+
const snapshot = this.reconcileHistory(lines);
|
|
229
|
+
const current = this.observeSubmissions(lines, snapshot, evidence);
|
|
230
|
+
this.retireNonSubmittingAttempts(result);
|
|
231
|
+
let aggregateComplete = false;
|
|
232
|
+
if (!evidence) {
|
|
233
|
+
const fork = resolveProvider(this.driver).fork;
|
|
234
|
+
const latest = fork.latestSubmitIndex(lines);
|
|
235
|
+
aggregateComplete = latest >= this.baseline && fork.turnComplete(lines, latest);
|
|
207
236
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
mapNewSubmits(observedSubmitCount, currentSubmitCount, snapshot) {
|
|
213
|
-
for (let submitOrdinal = observedSubmitCount + 1; submitOrdinal <= currentSubmitCount; submitOrdinal++) {
|
|
214
|
-
const turnIndex = this.pendingConfirmedTurns.findIndex((turn) => turn.anchorSubmitCount !== undefined
|
|
215
|
-
&& submitOrdinal > turn.anchorSubmitCount);
|
|
216
|
-
const attemptIndex = this.pendingTurnAttempts.findIndex((attempt) => attempt.anchorSubmitCount !== undefined
|
|
217
|
-
&& submitOrdinal > attempt.anchorSubmitCount);
|
|
218
|
-
const turn = turnIndex >= 0
|
|
219
|
-
? this.pendingConfirmedTurns[turnIndex]
|
|
220
|
-
: undefined;
|
|
221
|
-
const attempt = attemptIndex >= 0
|
|
222
|
-
? this.pendingTurnAttempts[attemptIndex]
|
|
223
|
-
: undefined;
|
|
224
|
-
if (turn && (!attempt || turn.sequence < attempt.sequence)) {
|
|
225
|
-
this.pendingConfirmedTurns.splice(turnIndex, 1);
|
|
226
|
-
this.mappedGeneration++;
|
|
227
|
-
this.rememberMappedSubmitIdentity(snapshot, submitOrdinal);
|
|
237
|
+
const offset = evidence ? evidence.submissions.length - current : 0;
|
|
238
|
+
for (const entry of this.entries) {
|
|
239
|
+
if (entry.ordinal === undefined || entry.settlement)
|
|
228
240
|
continue;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
this.pendingTurnAttempts.splice(attemptIndex, 1);
|
|
235
|
-
if (attempt.timer)
|
|
236
|
-
clearTimeout(attempt.timer);
|
|
237
|
-
this.turnGeneration++;
|
|
238
|
-
this.mappedGeneration++;
|
|
239
|
-
this.rememberMappedSubmitIdentity(snapshot, submitOrdinal);
|
|
240
|
-
continue;
|
|
241
|
+
entry.settlement = evidence
|
|
242
|
+
? evidence.submissions[entry.ordinal - 1 + offset]?.settlement
|
|
243
|
+
: aggregateComplete ? "completed" : undefined;
|
|
241
244
|
}
|
|
242
|
-
this.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
this.mappedGeneration++;
|
|
247
|
-
this.unreservedObservedGenerations.push(this.mappedGeneration);
|
|
248
|
-
this.rememberMappedSubmitIdentity(snapshot, submitOrdinal);
|
|
249
|
-
if (!this.busy) {
|
|
250
|
-
this.busy = true;
|
|
251
|
-
this.options.onChange?.(false);
|
|
245
|
+
if (this.entries.some((entry) => entry.ordinal !== undefined)
|
|
246
|
+
&& this.entries.every((entry) => entry.ordinal === undefined || entry.settlement)) {
|
|
247
|
+
this.submitFloor = current;
|
|
248
|
+
this.baseline = lines.length;
|
|
252
249
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const generation = this.unreservedObservedGenerations.shift();
|
|
256
|
-
if (generation === undefined)
|
|
257
|
-
return false;
|
|
258
|
-
if (generation > this.coveredGeneration && !this.busy) {
|
|
259
|
-
this.busy = true;
|
|
260
|
-
this.options.onChange?.(false);
|
|
250
|
+
this.updateActivity();
|
|
251
|
+
if (this.busy)
|
|
261
252
|
this.ensurePolling();
|
|
262
|
-
|
|
253
|
+
for (const entry of this.entries) {
|
|
254
|
+
if (this.stopped)
|
|
255
|
+
break;
|
|
256
|
+
if (entry.settlement)
|
|
257
|
+
entry.receipt?.settle(entry.settlement);
|
|
263
258
|
}
|
|
264
|
-
this.releaseIfSettled();
|
|
265
|
-
return true;
|
|
266
259
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (identity)
|
|
270
|
-
this.mappedSubmitIdentities.push(identity);
|
|
260
|
+
jsonlRuntimeDisabled() {
|
|
261
|
+
return resolveProvider(this.driver).forkSubmitFloor === undefined;
|
|
271
262
|
}
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (regression) {
|
|
285
|
-
this.applyHistoryRegression(regression, current);
|
|
286
|
-
this.historySnapshot = current;
|
|
287
|
-
this.options.onHistoryRegression?.(regression, current);
|
|
288
|
-
return current;
|
|
289
|
-
}
|
|
263
|
+
observeSubmissions(lines, snapshot, evidence) {
|
|
264
|
+
const { current, prior, assignments } = this.prepareObservation(lines);
|
|
265
|
+
for (const entry of this.entries)
|
|
266
|
+
entry.anchor ??= prior;
|
|
267
|
+
for (const [entry, ordinal] of assignments) {
|
|
268
|
+
entry.ordinal = ordinal;
|
|
269
|
+
entry.identity = evidence?.submissions[ordinal - 1 + evidence.submissions.length - current]?.identity
|
|
270
|
+
?? snapshot?.submitIdentities[ordinal - 1 + snapshot.submitCount - current];
|
|
271
|
+
if (entry.timer)
|
|
272
|
+
clearTimeout(entry.timer);
|
|
273
|
+
if (!this.entries.includes(entry))
|
|
274
|
+
this.entries.push(entry);
|
|
290
275
|
}
|
|
291
|
-
|
|
292
|
-
this.
|
|
293
|
-
|
|
294
|
-
this.options.onHistorySnapshot?.(current);
|
|
276
|
+
this.observedSubmitCount = current;
|
|
277
|
+
this.updateActivity();
|
|
278
|
+
this.warnAmbiguousOwnership();
|
|
295
279
|
return current;
|
|
296
280
|
}
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
continue;
|
|
304
|
-
this.mappedSubmitIdentities.splice(index, 1);
|
|
305
|
-
removedMapped++;
|
|
306
|
-
}
|
|
307
|
-
if (removedMapped > 0) {
|
|
308
|
-
this.mappedGeneration = Math.max(0, this.mappedGeneration - removedMapped);
|
|
309
|
-
this.coveredGeneration = Math.min(this.mappedGeneration, Math.max(0, this.coveredGeneration - removedMapped));
|
|
310
|
-
this.turnGeneration = Math.max(this.pendingTurnAttempts.length + this.pendingConfirmedTurns.length, this.turnGeneration - removedMapped);
|
|
281
|
+
prepareObservation(lines) {
|
|
282
|
+
const current = this.countSubmits(lines);
|
|
283
|
+
const prior = this.observedSubmitCount ?? this.submitFloor
|
|
284
|
+
?? this.countSubmits(lines.slice(0, this.baseline));
|
|
285
|
+
if (current < prior || this.baseline > lines.length) {
|
|
286
|
+
throw new Error("Session submission history regressed without a provider history boundary");
|
|
311
287
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
288
|
+
const pending = this.entries
|
|
289
|
+
.filter((entry) => entry.ordinal === undefined)
|
|
290
|
+
.sort((a, b) => a.receipt.sequence - b.receipt.sequence);
|
|
291
|
+
const assignments = new Map();
|
|
292
|
+
for (let ordinal = prior + 1; ordinal <= current; ordinal++) {
|
|
293
|
+
const index = pending.findIndex((entry) => ordinal > (entry.anchor ?? prior));
|
|
294
|
+
const entry = index < 0 ? {} : pending.splice(index, 1)[0];
|
|
295
|
+
assignments.set(entry, ordinal);
|
|
319
296
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
297
|
+
return { current, prior, assignments };
|
|
298
|
+
}
|
|
299
|
+
warnAmbiguousOwnership(entries = this.entries) {
|
|
300
|
+
const ambiguous = entries.some(({ receipt, ordinal }) => {
|
|
301
|
+
if (receipt?.settlement !== "expired" || ordinal === undefined)
|
|
302
|
+
return false;
|
|
303
|
+
return entries.some((newer) => newer.ordinal === undefined && newer.receipt && !newer.receipt.settlement
|
|
304
|
+
&& newer.receipt.sequence > receipt.sequence
|
|
305
|
+
&& newer.anchor !== undefined && newer.anchor < ordinal);
|
|
306
|
+
});
|
|
307
|
+
if (ambiguous && !this.ownershipWarned && !this.stopped) {
|
|
308
|
+
this.options.onReadFailure?.(new Error("Session submission ownership is ambiguous after an expired input attempt"));
|
|
324
309
|
}
|
|
325
|
-
this.
|
|
310
|
+
this.ownershipWarned = ambiguous;
|
|
326
311
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
312
|
+
retireNonSubmittingAttempts(read) {
|
|
313
|
+
if (this.stopped || read.registrationCount !== this.attached.size)
|
|
314
|
+
return;
|
|
315
|
+
let removed = false;
|
|
316
|
+
for (const entry of read.expiredAttempts) {
|
|
317
|
+
if (entry.ordinal !== undefined || !this.entries.includes(entry))
|
|
331
318
|
continue;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
319
|
+
this.removeEntry(entry);
|
|
320
|
+
removed = true;
|
|
321
|
+
}
|
|
322
|
+
if (removed)
|
|
323
|
+
this.updateActivity(true);
|
|
324
|
+
}
|
|
325
|
+
validateEvidence(lines, allowPending = false) {
|
|
326
|
+
const evidence = resolveProvider(this.driver).fork.submissionEvidence?.(lines);
|
|
327
|
+
if (evidence?.indeterminate && !(allowPending && evidence.indeterminateReason === "pending-submission")) {
|
|
328
|
+
throw new Error("Session submission evidence is indeterminate");
|
|
335
329
|
}
|
|
336
|
-
|
|
330
|
+
return evidence;
|
|
331
|
+
}
|
|
332
|
+
inspectHistory(lines) {
|
|
333
|
+
const fork = resolveProvider(this.driver).fork;
|
|
334
|
+
if (!fork.sessionHistorySnapshot)
|
|
335
|
+
return {};
|
|
336
|
+
const current = fork.sessionHistorySnapshot(lines);
|
|
337
|
+
if (!current)
|
|
338
|
+
throw new Error("Session history snapshot is unbuildable");
|
|
339
|
+
const previous = this.historySnapshot;
|
|
340
|
+
const regression = previous && fork.compareSessionHistory?.(previous, current);
|
|
341
|
+
return { snapshot: current, regression };
|
|
337
342
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
343
|
+
reconcileHistory(lines) {
|
|
344
|
+
const { snapshot: current, regression } = this.inspectHistory(lines);
|
|
345
|
+
if (!current)
|
|
346
|
+
return undefined;
|
|
347
|
+
const previous = this.historySnapshot;
|
|
348
|
+
if (regression) {
|
|
349
|
+
const identities = new Set(current.submitIdentities);
|
|
350
|
+
for (const entry of [...this.entries]) {
|
|
351
|
+
if (entry.identity && !identities.has(entry.identity)) {
|
|
352
|
+
this.removeEntry(entry);
|
|
353
|
+
entry.receipt?.settle("removed");
|
|
354
|
+
}
|
|
355
|
+
else if (entry.ordinal === undefined && entry.anchor !== undefined) {
|
|
356
|
+
entry.anchor = Math.min(entry.anchor, regression.commonSubmitCount);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
this.baseline = 0;
|
|
360
|
+
this.submitFloor = Math.min(this.submitFloor ?? regression.commonSubmitCount, regression.commonSubmitCount);
|
|
361
|
+
this.observedSubmitCount = Math.min(this.observedSubmitCount ?? regression.commonSubmitCount, regression.commonSubmitCount);
|
|
362
|
+
this.historySnapshot = current;
|
|
363
|
+
if (!this.stopped)
|
|
364
|
+
this.options.onHistoryRegression?.(regression, current);
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
this.historySnapshot = current;
|
|
368
|
+
if (!this.stopped && !sameHistorySnapshot(previous, current))
|
|
369
|
+
this.options.onHistorySnapshot?.(current);
|
|
341
370
|
}
|
|
371
|
+
return current;
|
|
372
|
+
}
|
|
373
|
+
removeEntry(entry) {
|
|
374
|
+
if (entry.timer)
|
|
375
|
+
clearTimeout(entry.timer);
|
|
376
|
+
entry.unsubscribe?.();
|
|
377
|
+
const index = this.entries.indexOf(entry);
|
|
378
|
+
if (index >= 0)
|
|
379
|
+
this.entries.splice(index, 1);
|
|
380
|
+
}
|
|
381
|
+
hasPendingAttempts() {
|
|
382
|
+
return this.entries.some((entry) => entry.receipt?.rawAttempt && !entry.receipt.settlement
|
|
383
|
+
&& entry.ordinal === undefined);
|
|
342
384
|
}
|
|
343
|
-
|
|
344
|
-
for (const resolve of this.
|
|
385
|
+
resolveAttemptWaiters() {
|
|
386
|
+
for (const resolve of this.attemptWaiters)
|
|
345
387
|
resolve();
|
|
346
|
-
this.
|
|
388
|
+
this.attemptWaiters.clear();
|
|
347
389
|
}
|
|
348
|
-
|
|
349
|
-
if (this.
|
|
350
|
-
|| this.pendingTurnAttempts.length > 0) {
|
|
390
|
+
updateActivity(notifyIfAlreadyIdle = false) {
|
|
391
|
+
if (this.attachingBatch)
|
|
351
392
|
return;
|
|
393
|
+
if (!this.hasPendingAttempts())
|
|
394
|
+
this.resolveAttemptWaiters();
|
|
395
|
+
const busy = this.entries.some((entry) => entry.ordinal === undefined
|
|
396
|
+
? !entry.receipt?.settlement : !entry.settlement);
|
|
397
|
+
const changed = busy !== this.busy;
|
|
398
|
+
this.busy = busy;
|
|
399
|
+
if (!busy && !this.supportsHistoryRegression && !this.hasUnobservedExpiredAttempts && this.timer) {
|
|
400
|
+
clearInterval(this.timer);
|
|
401
|
+
this.timer = undefined;
|
|
352
402
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if (!this.supportsHistoryRegression)
|
|
356
|
-
this.dispose();
|
|
357
|
-
if ((wasBusy || options.notifyIfAlreadyIdle === true) && !this.stopped) {
|
|
358
|
-
this.options.onChange?.(true);
|
|
403
|
+
if (!this.stopped && (changed || (!busy && notifyIfAlreadyIdle))) {
|
|
404
|
+
this.options.onChange?.(!busy);
|
|
359
405
|
}
|
|
360
406
|
}
|
|
361
407
|
ensurePolling() {
|
|
362
|
-
if (this.timer)
|
|
408
|
+
if (this.stopped || this.timer)
|
|
363
409
|
return;
|
|
364
|
-
this.timer = setInterval(() => {
|
|
410
|
+
this.timer = setInterval(() => { void this.pollNow(); }, this.pollMs);
|
|
411
|
+
if (this.busy || this.supportsHistoryRegression)
|
|
365
412
|
void this.pollNow();
|
|
366
|
-
}, this.pollMs);
|
|
367
|
-
void this.pollNow();
|
|
368
413
|
}
|
|
369
|
-
async
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
414
|
+
async readValidResult() {
|
|
415
|
+
// Only a post-expiry read, uninterrupted by newer input, can release ownership.
|
|
416
|
+
const expiredAttempts = this.entries.filter(({ receipt, ordinal }) => ordinal === undefined && receipt?.settlement === "expired"
|
|
417
|
+
&& !this.entries.some((newer) => newer.receipt && !newer.receipt.settlement
|
|
418
|
+
&& newer.receipt.sequence > receipt.sequence));
|
|
419
|
+
const registrationCount = this.attached.size;
|
|
420
|
+
const result = this.options.readResult
|
|
421
|
+
? await this.options.readResult(this.sessionFilePath)
|
|
422
|
+
: this.options.readLines
|
|
423
|
+
? { lines: await this.options.readLines(this.sessionFilePath), malformed: false }
|
|
424
|
+
: await readSessionFileWithStatus(this.sessionFilePath);
|
|
425
|
+
if (result.malformed)
|
|
426
|
+
throw new Error("Session log contains malformed JSONL");
|
|
427
|
+
return { ...result, expiredAttempts, registrationCount };
|
|
380
428
|
}
|
|
381
429
|
countSubmits(lines) {
|
|
382
430
|
return this.options.submitCount
|
|
@@ -385,9 +433,7 @@ export class SessionTurnMonitor {
|
|
|
385
433
|
}
|
|
386
434
|
}
|
|
387
435
|
function sameHistorySnapshot(left, right) {
|
|
388
|
-
|
|
389
|
-
return false;
|
|
390
|
-
return left.lineCount === right.lineCount
|
|
436
|
+
return left !== undefined && left.lineCount === right.lineCount
|
|
391
437
|
&& left.submitCount === right.submitCount
|
|
392
438
|
&& left.submitIdentities.length === right.submitIdentities.length
|
|
393
439
|
&& left.submitIdentities.every((identity, index) => identity === right.submitIdentities[index]);
|