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
|
@@ -3,14 +3,15 @@ import { builtinStrategies, resolveProvider } from "../providers/registry.js";
|
|
|
3
3
|
import { Fork } from "./fork.js";
|
|
4
4
|
import { foldAltMetaKeys, isPrimaryIssueKey, isPrimaryShowKey, isSecondaryIssueKey, isSecondaryShowKey, isSummarizeKey, } from "./keys.js";
|
|
5
5
|
import { InquiryPaneController } from "./inquiry-pane.js";
|
|
6
|
+
import { StartingInquiryPane } from "./starting-pane.js";
|
|
6
7
|
import { ArtifactPaneController } from "./artifact-pane.js";
|
|
7
8
|
import { registerOffscreenDisposer } from "./registry.js";
|
|
8
9
|
import { SplitInquiryCoordinator } from "./split-inquiry.js";
|
|
9
10
|
import { SPLIT_COLLAPSE_AT_COLS, } from "../user/split-surface.js";
|
|
10
11
|
import { sendInquiryQuestion } from "./inquiry-sender.js";
|
|
11
|
-
import {
|
|
12
|
+
import { readSessionFileWithStatus, } from "../session/reader.js";
|
|
12
13
|
import { forkFileSubmitFloor, locateForkSessionFile, submitCount, } from "./fork-busy.js";
|
|
13
|
-
import { SessionTurnMonitor, } from "./session-turn-monitor.js";
|
|
14
|
+
import { SessionTurnMonitor, SessionTurnReceipt, DEFAULT_TURN_ATTEMPT_TIMEOUT_MS, } from "./session-turn-monitor.js";
|
|
14
15
|
import { abortableSleep } from "../util/abortable-sleep.js";
|
|
15
16
|
const OFFSCREEN_MOUSE_SOURCE = "offscreen";
|
|
16
17
|
const DEFAULT_TURN_POLL_MS = 500;
|
|
@@ -19,6 +20,7 @@ export class OffscreenController {
|
|
|
19
20
|
host;
|
|
20
21
|
workdir;
|
|
21
22
|
inquiry;
|
|
23
|
+
observerRuns;
|
|
22
24
|
slots = new Map();
|
|
23
25
|
artifactConfigs;
|
|
24
26
|
turnPollMs;
|
|
@@ -26,6 +28,9 @@ export class OffscreenController {
|
|
|
26
28
|
tracksTurns;
|
|
27
29
|
forks = new Map();
|
|
28
30
|
pending = new Map();
|
|
31
|
+
pendingForks = new Map();
|
|
32
|
+
pendingQuestions = new Map();
|
|
33
|
+
launchAbort = new AbortController();
|
|
29
34
|
sendPending = new Set();
|
|
30
35
|
turnInProgress = new Set();
|
|
31
36
|
turnMonitors = new Map();
|
|
@@ -36,36 +41,40 @@ export class OffscreenController {
|
|
|
36
41
|
forkBaselines = new Map();
|
|
37
42
|
forkLocateAborts = new Map();
|
|
38
43
|
forkSubmitFloors = new Map();
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
pendingTurns = new Map();
|
|
45
|
+
pendingAttemptTimers = new Map();
|
|
41
46
|
forkSubmitFloorReads = new Map();
|
|
42
47
|
forkLocateWarned = new Set();
|
|
43
48
|
activeArtifactIndexes = new Map();
|
|
49
|
+
artifactPanes = new Map();
|
|
44
50
|
unregister;
|
|
45
51
|
split;
|
|
46
52
|
shown;
|
|
47
53
|
disposed = false;
|
|
48
|
-
|
|
54
|
+
disposal;
|
|
55
|
+
constructor(host, workdir, slots, artifactConfigs = {}, inquiry, observerRuns) {
|
|
49
56
|
this.host = host;
|
|
50
57
|
this.workdir = workdir;
|
|
51
58
|
this.inquiry = inquiry;
|
|
59
|
+
this.observerRuns = observerRuns;
|
|
52
60
|
for (const slot of slots) {
|
|
53
61
|
this.slots.set(slot.binding, slot);
|
|
54
62
|
}
|
|
55
63
|
// Snapshot the configs so later caller mutation can't reach the controller.
|
|
56
64
|
this.artifactConfigs = { ...artifactConfigs };
|
|
57
65
|
this.turnPollMs = inquiry?.turnPollMs ?? DEFAULT_TURN_POLL_MS;
|
|
58
|
-
this.turnAttemptTimeoutMs = inquiry?.turnAttemptTimeoutMs;
|
|
66
|
+
this.turnAttemptTimeoutMs = inquiry?.turnAttemptTimeoutMs ?? DEFAULT_TURN_ATTEMPT_TIMEOUT_MS;
|
|
59
67
|
this.tracksTurns =
|
|
60
68
|
inquiry !== undefined || slots.some((slot) => slot.summarizePrompt !== undefined);
|
|
61
69
|
this.unregister = registerOffscreenDisposer(() => this.dispose());
|
|
62
70
|
}
|
|
63
71
|
isIssueInquiryReady(binding) {
|
|
64
|
-
return !this.sendPending.has(binding) && !this.turnInProgress.has(binding)
|
|
72
|
+
return !this.sendPending.has(binding) && !this.turnInProgress.has(binding)
|
|
73
|
+
&& !this.sendLocks.has(binding);
|
|
65
74
|
}
|
|
66
75
|
selectionDelegate() {
|
|
67
76
|
return {
|
|
68
|
-
fork: this.shown ? this.
|
|
77
|
+
fork: this.shown ? this.presentedFork(this.shown) : undefined,
|
|
69
78
|
layout: this.host.getLayout(),
|
|
70
79
|
coordinator: this.activeCoordinator(),
|
|
71
80
|
};
|
|
@@ -73,15 +82,16 @@ export class OffscreenController {
|
|
|
73
82
|
wrap(base) {
|
|
74
83
|
return {
|
|
75
84
|
getSurface: () => {
|
|
76
|
-
const fork = this.shown ? this.
|
|
77
|
-
if (!
|
|
85
|
+
const fork = this.shown ? this.presentedFork(this.shown) : undefined;
|
|
86
|
+
if (!this.shown)
|
|
78
87
|
return base.getSurface();
|
|
79
88
|
const layout = this.host.getLayout();
|
|
80
89
|
const coordinator = this.activeCoordinator();
|
|
81
90
|
if (coordinator) {
|
|
82
91
|
return coordinator.getSurface(layout.columns, layout.childRows);
|
|
83
92
|
}
|
|
84
|
-
return new InquiryPaneController(fork
|
|
93
|
+
return (fork ? new InquiryPaneController(fork, this.forks.has(this.shown)) : this.startingPane(this.shown))
|
|
94
|
+
.render(layout.columns, layout.childRows);
|
|
85
95
|
},
|
|
86
96
|
selectionRegionAt: (column, row) => {
|
|
87
97
|
const { fork, layout, coordinator } = this.selectionDelegate();
|
|
@@ -89,7 +99,7 @@ export class OffscreenController {
|
|
|
89
99
|
return coordinator.selectionRegionAt(column, row, layout.columns, layout.childRows);
|
|
90
100
|
}
|
|
91
101
|
if (!fork)
|
|
92
|
-
return base.selectionRegionAt?.(column, row);
|
|
102
|
+
return this.shown ? null : base.selectionRegionAt?.(column, row);
|
|
93
103
|
if (row < 2 || row > layout.childRows)
|
|
94
104
|
return null;
|
|
95
105
|
return fork.vt.isAlternate === true
|
|
@@ -102,7 +112,7 @@ export class OffscreenController {
|
|
|
102
112
|
return coordinator.selectionTargetAt(column, row, layout.columns, layout.childRows);
|
|
103
113
|
}
|
|
104
114
|
if (!fork)
|
|
105
|
-
return base.selectionTargetAt?.(column, row) ?? null;
|
|
115
|
+
return this.shown ? null : base.selectionTargetAt?.(column, row) ?? null;
|
|
106
116
|
if (fork.vt.isAlternate === true)
|
|
107
117
|
return null;
|
|
108
118
|
if (column < 1 || column > layout.columns || row < 2 || row > layout.childRows)
|
|
@@ -115,7 +125,7 @@ export class OffscreenController {
|
|
|
115
125
|
return coordinator.scrollSelectableSurfaceAt(column, row, direction, layout.columns, layout.childRows);
|
|
116
126
|
}
|
|
117
127
|
if (!fork || fork.vt.isAlternate === true) {
|
|
118
|
-
return base.scrollSelectableSurfaceAt?.(column, row, direction) ?? 0;
|
|
128
|
+
return this.shown ? 0 : base.scrollSelectableSurfaceAt?.(column, row, direction) ?? 0;
|
|
119
129
|
}
|
|
120
130
|
if (column < 1 || column > layout.columns)
|
|
121
131
|
return 0;
|
|
@@ -133,7 +143,7 @@ export class OffscreenController {
|
|
|
133
143
|
return placement ? { region: placement.region, vt: fork.vt } : undefined;
|
|
134
144
|
}
|
|
135
145
|
if (!fork || fork.vt.isAlternate === true || layout.childRows < 2) {
|
|
136
|
-
return base.forkSelectionPlacement?.();
|
|
146
|
+
return this.shown ? undefined : base.forkSelectionPlacement?.();
|
|
137
147
|
}
|
|
138
148
|
return {
|
|
139
149
|
region: { rowStart: 2, rowEnd: layout.childRows, colStart: 1, colEnd: layout.columns },
|
|
@@ -145,7 +155,7 @@ export class OffscreenController {
|
|
|
145
155
|
if (coordinator) {
|
|
146
156
|
return coordinator.artifactPlacementAt(column, row, layout.columns, layout.childRows);
|
|
147
157
|
}
|
|
148
|
-
if (
|
|
158
|
+
if (this.shown)
|
|
149
159
|
return undefined;
|
|
150
160
|
return base.docSurfaceSelectionPlacementAt?.(column, row);
|
|
151
161
|
},
|
|
@@ -155,7 +165,7 @@ export class OffscreenController {
|
|
|
155
165
|
const placement = coordinator.artifactPlacement(layout.columns, layout.childRows);
|
|
156
166
|
return placement ? [placement] : [];
|
|
157
167
|
}
|
|
158
|
-
if (
|
|
168
|
+
if (this.shown)
|
|
159
169
|
return [];
|
|
160
170
|
return base.docSurfaceSelectionPlacements?.() ?? [];
|
|
161
171
|
},
|
|
@@ -164,11 +174,13 @@ export class OffscreenController {
|
|
|
164
174
|
if (coordinator) {
|
|
165
175
|
return coordinator.artifactPlacement(layout.columns, layout.childRows);
|
|
166
176
|
}
|
|
167
|
-
if (
|
|
177
|
+
if (this.shown)
|
|
168
178
|
return undefined;
|
|
169
179
|
return base.artifactSelectionPlacement?.();
|
|
170
180
|
},
|
|
171
181
|
onEvents: (keys) => {
|
|
182
|
+
if (this.disposed)
|
|
183
|
+
return true;
|
|
172
184
|
const events = this.inquiry ? foldAltMetaKeys(keys) : keys;
|
|
173
185
|
if (keys.some(isSummarizeKey) && this.canSummarize("primary")) {
|
|
174
186
|
return this.summarize("primary");
|
|
@@ -200,41 +212,60 @@ export class OffscreenController {
|
|
|
200
212
|
onResize: (cols, rows) => {
|
|
201
213
|
if (this.disposed)
|
|
202
214
|
return;
|
|
203
|
-
const fork = this.shown ? this.
|
|
204
|
-
if (
|
|
215
|
+
const fork = this.shown ? this.presentedFork(this.shown) : undefined;
|
|
216
|
+
if (this.shown)
|
|
205
217
|
this.configurePresentation(this.shown, fork, cols, rows);
|
|
206
218
|
this.updateForkState();
|
|
207
219
|
},
|
|
208
220
|
};
|
|
209
221
|
}
|
|
210
222
|
dispose() {
|
|
211
|
-
if (this.
|
|
212
|
-
return;
|
|
223
|
+
if (this.disposal)
|
|
224
|
+
return this.disposal;
|
|
213
225
|
this.disposed = true;
|
|
226
|
+
this.launchAbort.abort();
|
|
214
227
|
this.unregister();
|
|
215
228
|
for (const binding of Array.from(this.sendControllers.keys())) {
|
|
216
229
|
this.abortIssueSend(binding);
|
|
217
230
|
}
|
|
218
231
|
this.clearForkSessionWatchers();
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
232
|
+
const releases = [...this.forks.values(), ...this.pendingForks.values()].map((fork) => fork.kill());
|
|
233
|
+
const pending = Array.from(this.pending.values());
|
|
222
234
|
this.forks.clear();
|
|
223
235
|
this.pending.clear();
|
|
236
|
+
this.pendingForks.clear();
|
|
237
|
+
this.pendingQuestions.clear();
|
|
238
|
+
this.artifactPanes.clear();
|
|
224
239
|
this.sendPending.clear();
|
|
225
240
|
this.sendLocks.clear();
|
|
226
241
|
this.shown = undefined;
|
|
227
242
|
this.clearSplitCoordinator();
|
|
228
243
|
this.host.clearMouseModeSource(OFFSCREEN_MOUSE_SOURCE);
|
|
229
244
|
this.updateForkState();
|
|
245
|
+
return this.disposal = Promise.all([...releases, ...pending]).then(() => { });
|
|
230
246
|
}
|
|
231
247
|
handleForkEvents(keys) {
|
|
232
248
|
const binding = this.shown;
|
|
233
249
|
const fork = binding ? this.forks.get(binding) : undefined;
|
|
234
250
|
if (!fork) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
251
|
+
if (!binding)
|
|
252
|
+
return true;
|
|
253
|
+
if (keys.some((key) => key.kind === "control" && key.byte === 0x03)) {
|
|
254
|
+
this.hideFork(this.pendingForks.get(binding));
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
for (const key of keys) {
|
|
258
|
+
if (isArtifactViewerKey(key) && this.cycleCompanionArtifact(binding))
|
|
259
|
+
continue;
|
|
260
|
+
const shortcut = this.inquiry ? inquiryShortcutForKey(key) : undefined;
|
|
261
|
+
if (shortcut?.kind === "issue" && shortcut.binding === binding) {
|
|
262
|
+
this.openInquiry(shortcut);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const layout = this.host.getLayout();
|
|
266
|
+
const result = this.activeCoordinator()?.handleArtifactScroll(keys, layout.columns, layout.childRows);
|
|
267
|
+
if (result)
|
|
268
|
+
this.applyPaneResult(undefined, result);
|
|
238
269
|
return true;
|
|
239
270
|
}
|
|
240
271
|
if (binding && this.sendLocks.has(binding)) {
|
|
@@ -285,16 +316,16 @@ export class OffscreenController {
|
|
|
285
316
|
return true;
|
|
286
317
|
if (routed.length === 0)
|
|
287
318
|
return true;
|
|
288
|
-
const coordinator = this.activeCoordinator();
|
|
289
|
-
const layout = this.host.getLayout();
|
|
290
|
-
const result = coordinator
|
|
291
|
-
? coordinator.handleEvents(routed, layout.columns, layout.childRows)
|
|
292
|
-
: new InquiryPaneController(fork).handleKeys(routed);
|
|
293
319
|
if (this.tracksTurns && binding) {
|
|
294
320
|
for (let submission = 0; submission < manualSubmissions; submission++) {
|
|
295
321
|
this.beginTurnAttempt(binding);
|
|
296
322
|
}
|
|
297
323
|
}
|
|
324
|
+
const coordinator = this.activeCoordinator();
|
|
325
|
+
const layout = this.host.getLayout();
|
|
326
|
+
const result = coordinator
|
|
327
|
+
? coordinator.handleEvents(routed, layout.columns, layout.childRows)
|
|
328
|
+
: new InquiryPaneController(fork).handleKeys(routed);
|
|
298
329
|
this.applyPaneResult(fork, result);
|
|
299
330
|
return true;
|
|
300
331
|
}
|
|
@@ -334,14 +365,17 @@ export class OffscreenController {
|
|
|
334
365
|
this.hideFork(fork);
|
|
335
366
|
}
|
|
336
367
|
}
|
|
368
|
+
presentedFork(binding) {
|
|
369
|
+
return this.forks.get(binding) ?? this.pendingForks.get(binding);
|
|
370
|
+
}
|
|
337
371
|
hideFork(fork) {
|
|
338
372
|
if (this.shown)
|
|
339
373
|
this.abortIssueSend(this.shown);
|
|
340
374
|
if (this.activeCoordinator()) {
|
|
341
375
|
const layout = this.host.getLayout();
|
|
342
|
-
fork
|
|
376
|
+
fork?.resize(layout.columns, layout.childRows);
|
|
343
377
|
}
|
|
344
|
-
fork
|
|
378
|
+
fork?.setVisible(false);
|
|
345
379
|
this.shown = undefined;
|
|
346
380
|
this.clearSplitCoordinator();
|
|
347
381
|
this.host.clearMouseModeSource(OFFSCREEN_MOUSE_SOURCE);
|
|
@@ -374,7 +408,7 @@ export class OffscreenController {
|
|
|
374
408
|
];
|
|
375
409
|
}
|
|
376
410
|
nextCompanionArtifactHint(binding) {
|
|
377
|
-
if (this.shown !== binding
|
|
411
|
+
if (this.shown !== binding)
|
|
378
412
|
return undefined;
|
|
379
413
|
const config = this.artifactConfigs[binding];
|
|
380
414
|
if (!config)
|
|
@@ -387,8 +421,8 @@ export class OffscreenController {
|
|
|
387
421
|
}
|
|
388
422
|
cycleCompanionArtifact(binding) {
|
|
389
423
|
const primary = this.artifactConfigs[binding];
|
|
390
|
-
const fork = this.
|
|
391
|
-
if (!primary
|
|
424
|
+
const fork = this.presentedFork(binding);
|
|
425
|
+
if (!primary)
|
|
392
426
|
return false;
|
|
393
427
|
const count = this.companionArtifacts(primary).length;
|
|
394
428
|
if (count <= 1)
|
|
@@ -404,21 +438,33 @@ export class OffscreenController {
|
|
|
404
438
|
configurePresentation(binding, fork, cols, rows) {
|
|
405
439
|
const artifact = this.activeArtifactConfig(binding);
|
|
406
440
|
if (artifact && (cols >= SPLIT_COLLAPSE_AT_COLS || artifact.collapseWhenNarrow)) {
|
|
441
|
+
const index = this.activeArtifactIndexes.get(binding) ?? 0;
|
|
442
|
+
let panes = this.artifactPanes.get(binding);
|
|
443
|
+
if (!panes)
|
|
444
|
+
this.artifactPanes.set(binding, panes = new Map());
|
|
445
|
+
let artifactPane = panes.get(index);
|
|
446
|
+
if (!artifactPane) {
|
|
447
|
+
artifactPane = new ArtifactPaneController(artifact.renderArtifact, artifact.styleVersion);
|
|
448
|
+
panes.set(index, artifactPane);
|
|
449
|
+
}
|
|
407
450
|
this.split = {
|
|
408
451
|
binding,
|
|
409
452
|
coordinator: new SplitInquiryCoordinator({
|
|
410
453
|
inquirySide: artifact.inquirySide,
|
|
411
|
-
inquiryPane: new InquiryPaneController(fork),
|
|
412
|
-
artifactPane
|
|
454
|
+
inquiryPane: fork ? new InquiryPaneController(fork, this.forks.has(binding)) : this.startingPane(binding),
|
|
455
|
+
artifactPane,
|
|
413
456
|
collapseAt: SPLIT_COLLAPSE_AT_COLS,
|
|
414
457
|
collapseArtifact: artifact.collapseWhenNarrow,
|
|
415
458
|
}),
|
|
416
459
|
};
|
|
417
|
-
fork
|
|
460
|
+
fork?.resize(this.split.coordinator.inquiryWidth(cols), this.split.coordinator.inquiryHeight(cols, rows));
|
|
418
461
|
return;
|
|
419
462
|
}
|
|
420
463
|
this.clearSplitCoordinator();
|
|
421
|
-
fork
|
|
464
|
+
fork?.resize(cols, rows);
|
|
465
|
+
}
|
|
466
|
+
startingPane(binding) {
|
|
467
|
+
return new StartingInquiryPane(this.slots.get(binding).roleLabel);
|
|
422
468
|
}
|
|
423
469
|
focusArtifactForCurrentIssue(binding) {
|
|
424
470
|
if (this.shown !== binding)
|
|
@@ -444,60 +490,98 @@ export class OffscreenController {
|
|
|
444
490
|
this.host.scheduleParentUiRender();
|
|
445
491
|
return;
|
|
446
492
|
}
|
|
447
|
-
|
|
448
|
-
.
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
this.
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
this.host.scheduleRender();
|
|
493
|
+
if (this.shown && this.shown !== binding)
|
|
494
|
+
this.abortIssueSend(this.shown);
|
|
495
|
+
for (const [key, other] of [...this.forks, ...this.pendingForks]) {
|
|
496
|
+
if (key !== binding)
|
|
497
|
+
other.setVisible(false);
|
|
498
|
+
}
|
|
499
|
+
const fork = this.forks.get(binding);
|
|
500
|
+
const presented = this.presentedFork(binding);
|
|
501
|
+
const layout = this.host.getLayout();
|
|
502
|
+
this.shown = binding;
|
|
503
|
+
this.configurePresentation(binding, presented, layout.columns, layout.childRows);
|
|
504
|
+
if (focusIssueArtifact)
|
|
505
|
+
this.focusArtifactForCurrentIssue(binding);
|
|
506
|
+
presented?.setVisible(true);
|
|
507
|
+
this.updateForkState();
|
|
508
|
+
this.host.scheduleRender();
|
|
509
|
+
if (fork) {
|
|
465
510
|
if (question)
|
|
466
511
|
this.startInquirySend(binding, fork, question);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
this.
|
|
472
|
-
|
|
473
|
-
this.host.scheduleParentUiRender();
|
|
474
|
-
});
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
if (question)
|
|
515
|
+
this.pendingQuestions.set(binding, question);
|
|
516
|
+
this.ensureFork(binding, slot);
|
|
517
|
+
}
|
|
475
518
|
}
|
|
476
519
|
ensureFork(binding, slot) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
520
|
+
if (this.pending.has(binding))
|
|
521
|
+
return;
|
|
522
|
+
let constructed;
|
|
523
|
+
// The host's coalesced first frame must run before any provider preparation.
|
|
524
|
+
const created = new Promise((resolve) => setImmediate(resolve)).then(async () => {
|
|
525
|
+
this.launchAbort.signal.throwIfAborted();
|
|
526
|
+
const layout = this.host.getLayout();
|
|
527
|
+
return Fork.spawn(slot, this.workdir, this.host, layout.columns, layout.childRows, () => {
|
|
528
|
+
if (!this.disposed && constructed && this.presentedFork(binding) === constructed) {
|
|
529
|
+
this.updateForkState();
|
|
530
|
+
}
|
|
531
|
+
}, this.observerRuns, this.launchAbort.signal, (fork) => {
|
|
532
|
+
constructed = fork;
|
|
533
|
+
if (this.disposed || this.pending.get(binding) !== created) {
|
|
534
|
+
void fork.kill();
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
this.pendingForks.set(binding, fork);
|
|
538
|
+
if (this.shown !== binding)
|
|
539
|
+
return;
|
|
540
|
+
const layout = this.host.getLayout();
|
|
541
|
+
this.configurePresentation(binding, fork, layout.columns, layout.childRows);
|
|
542
|
+
fork.setVisible(true);
|
|
543
|
+
this.updateForkState();
|
|
544
|
+
this.host.scheduleParentUiRender();
|
|
545
|
+
});
|
|
546
|
+
}).then(async (fork) => {
|
|
547
|
+
if (this.disposed || this.pending.get(binding) !== created) {
|
|
548
|
+
await fork.kill();
|
|
549
|
+
return;
|
|
489
550
|
}
|
|
551
|
+
constructed = fork;
|
|
552
|
+
this.pendingForks.delete(binding);
|
|
490
553
|
this.forks.set(binding, fork);
|
|
491
554
|
if (this.tracksTurns) {
|
|
492
555
|
void this.ensureForkSession(binding, fork);
|
|
493
556
|
}
|
|
494
|
-
|
|
557
|
+
if (this.shown === binding) {
|
|
558
|
+
const layout = this.host.getLayout();
|
|
559
|
+
this.configurePresentation(binding, fork, layout.columns, layout.childRows);
|
|
560
|
+
fork.setVisible(true);
|
|
561
|
+
this.updateForkState();
|
|
562
|
+
this.host.scheduleRender();
|
|
563
|
+
}
|
|
564
|
+
const question = this.pendingQuestions.get(binding);
|
|
565
|
+
this.pendingQuestions.delete(binding);
|
|
566
|
+
if (question)
|
|
567
|
+
this.startInquirySend(binding, fork, question);
|
|
495
568
|
}).catch((err) => {
|
|
496
|
-
this.
|
|
497
|
-
|
|
569
|
+
if (this.disposed && err === this.launchAbort.signal.reason)
|
|
570
|
+
return;
|
|
571
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
572
|
+
this.host.output.warn(`offscreen ${slot.roleLabel} fork failed: ${reason}`);
|
|
573
|
+
if (this.disposed || this.pending.get(binding) !== created)
|
|
574
|
+
return;
|
|
575
|
+
this.pendingQuestions.delete(binding);
|
|
576
|
+
this.clearSendPending(binding);
|
|
577
|
+
this.pendingForks.delete(binding);
|
|
578
|
+
if (this.shown === binding)
|
|
579
|
+
this.hideFork();
|
|
580
|
+
}).finally(() => {
|
|
581
|
+
if (this.pending.get(binding) === created)
|
|
582
|
+
this.pending.delete(binding);
|
|
498
583
|
});
|
|
499
584
|
this.pending.set(binding, created);
|
|
500
|
-
return created;
|
|
501
585
|
}
|
|
502
586
|
startInquirySend(binding, fork, question) {
|
|
503
587
|
if (this.disposed || this.sendControllers.has(binding))
|
|
@@ -508,12 +592,22 @@ export class OffscreenController {
|
|
|
508
592
|
this.sendLocks.add(binding);
|
|
509
593
|
this.host.scheduleParentUiRender();
|
|
510
594
|
const composer = resolveProvider(fork.driver).composer;
|
|
595
|
+
let receipt;
|
|
511
596
|
void sendInquiryQuestion(fork, question, {
|
|
512
597
|
signal: controller.signal,
|
|
513
598
|
composer,
|
|
599
|
+
beforeSubmit: async () => {
|
|
600
|
+
await this.turnMonitors.get(binding)?.refreshExpiredAttempts();
|
|
601
|
+
controller.signal.throwIfAborted();
|
|
602
|
+
this.launchAbort.signal.throwIfAborted();
|
|
603
|
+
receipt = this.reserveTurn(binding);
|
|
604
|
+
},
|
|
605
|
+
onSubmitWritten: () => receipt?.markWritten(),
|
|
514
606
|
onSubmitted: () => {
|
|
515
607
|
submitted = true;
|
|
516
|
-
|
|
608
|
+
receipt?.confirm();
|
|
609
|
+
if (!this.disposed && this.sendControllers.get(binding) === controller)
|
|
610
|
+
this.clearSendPending(binding);
|
|
517
611
|
},
|
|
518
612
|
}).catch((err) => {
|
|
519
613
|
if (!controller.signal.aborted) {
|
|
@@ -521,9 +615,20 @@ export class OffscreenController {
|
|
|
521
615
|
this.host.output.warn(`offscreen inquiry send failed: ${reason}`);
|
|
522
616
|
}
|
|
523
617
|
}).finally(() => {
|
|
524
|
-
|
|
525
|
-
|
|
618
|
+
receipt?.release();
|
|
619
|
+
if (this.disposed)
|
|
620
|
+
return;
|
|
621
|
+
if (receipt?.settlement === "released") {
|
|
622
|
+
const remaining = (this.pendingTurns.get(binding) ?? []).filter((pending) => pending !== receipt);
|
|
623
|
+
if (remaining.length > 0)
|
|
624
|
+
this.pendingTurns.set(binding, remaining);
|
|
625
|
+
else
|
|
626
|
+
this.pendingTurns.delete(binding);
|
|
627
|
+
this.refreshStagedActivity(binding);
|
|
526
628
|
}
|
|
629
|
+
if (this.sendControllers.get(binding) !== controller)
|
|
630
|
+
return;
|
|
631
|
+
this.sendControllers.delete(binding);
|
|
527
632
|
this.sendLocks.delete(binding);
|
|
528
633
|
if (!submitted)
|
|
529
634
|
this.clearSendPending(binding);
|
|
@@ -539,44 +644,73 @@ export class OffscreenController {
|
|
|
539
644
|
clearSendPending(binding) {
|
|
540
645
|
if (!this.sendPending.delete(binding))
|
|
541
646
|
return;
|
|
542
|
-
this.
|
|
647
|
+
if (!this.disposed)
|
|
648
|
+
this.host.scheduleParentUiRender();
|
|
543
649
|
}
|
|
544
|
-
|
|
650
|
+
reserveTurn(binding, rawAttempt = false) {
|
|
545
651
|
const pendingChanged = this.sendPending.delete(binding);
|
|
546
652
|
const turnChanged = this.prepareTurn(binding);
|
|
547
653
|
const monitor = this.turnMonitors.get(binding);
|
|
548
|
-
|
|
549
|
-
|
|
654
|
+
let receipt;
|
|
655
|
+
if (monitor) {
|
|
656
|
+
receipt = rawAttempt ? monitor.beginTurnAttempt() : monitor.reserveTurn();
|
|
657
|
+
}
|
|
550
658
|
else {
|
|
551
|
-
|
|
659
|
+
receipt = new SessionTurnReceipt({
|
|
660
|
+
anchorSubmitCount: this.forkSubmitFloors.get(binding),
|
|
661
|
+
}, rawAttempt);
|
|
662
|
+
if (rawAttempt)
|
|
663
|
+
receipt.markWritten();
|
|
664
|
+
const pending = this.pendingTurns.get(binding) ?? [];
|
|
665
|
+
pending.push(receipt);
|
|
666
|
+
this.pendingTurns.set(binding, pending);
|
|
667
|
+
if (rawAttempt) {
|
|
668
|
+
this.pendingAttemptTimers.set(receipt.id, setTimeout(() => {
|
|
669
|
+
this.pendingAttemptTimers.delete(receipt.id);
|
|
670
|
+
if (this.disposed)
|
|
671
|
+
return;
|
|
672
|
+
receipt.settle("expired");
|
|
673
|
+
this.refreshStagedActivity(binding);
|
|
674
|
+
}, Math.max(0, receipt.startedAt + this.turnAttemptTimeoutMs - Date.now())));
|
|
675
|
+
}
|
|
552
676
|
this.ensureTurnPoll(binding);
|
|
553
677
|
}
|
|
554
678
|
if (pendingChanged || turnChanged)
|
|
555
679
|
this.host.scheduleParentUiRender();
|
|
680
|
+
return receipt;
|
|
556
681
|
}
|
|
557
682
|
beginTurnAttempt(binding) {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
});
|
|
568
|
-
this.pendingTurnAttempts.set(binding, attempts);
|
|
569
|
-
this.ensureTurnPoll(binding);
|
|
683
|
+
this.reserveTurn(binding, true);
|
|
684
|
+
}
|
|
685
|
+
refreshStagedActivity(binding) {
|
|
686
|
+
const pending = this.pendingTurns.get(binding) ?? [];
|
|
687
|
+
if (pending.some((receipt) => !receipt.settlement)
|
|
688
|
+
|| this.turnMonitors.get(binding)?.isIdle === false)
|
|
689
|
+
return;
|
|
690
|
+
if (pending.length === 0) {
|
|
691
|
+
this.releaseTurn(binding);
|
|
570
692
|
}
|
|
571
|
-
if (
|
|
693
|
+
else if (this.turnInProgress.delete(binding)) {
|
|
694
|
+
// Retain expired receipts until discovery so late genuine work is still observed.
|
|
572
695
|
this.host.scheduleParentUiRender();
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
needsTurnObservation(binding) {
|
|
699
|
+
return this.turnInProgress.has(binding) || this.pendingTurns.has(binding);
|
|
700
|
+
}
|
|
701
|
+
clearStagedAttemptTimers(binding) {
|
|
702
|
+
for (const receipt of this.pendingTurns.get(binding) ?? []) {
|
|
703
|
+
clearTimeout(this.pendingAttemptTimers.get(receipt.id));
|
|
704
|
+
this.pendingAttemptTimers.delete(receipt.id);
|
|
705
|
+
}
|
|
573
706
|
}
|
|
574
707
|
prepareTurn(binding) {
|
|
575
708
|
const turnChanged = !this.turnInProgress.has(binding);
|
|
576
709
|
this.turnInProgress.add(binding);
|
|
577
710
|
const fork = this.forks.get(binding);
|
|
578
|
-
if (!this.
|
|
579
|
-
|
|
711
|
+
if (!this.turnMonitors.has(binding)
|
|
712
|
+
&& (!this.forkSessionFiles.has(binding)
|
|
713
|
+
|| (fork && this.needsSubmitFloor(binding, fork)))) {
|
|
580
714
|
if (fork) {
|
|
581
715
|
this.forkLocateAborts.get(binding)?.abort();
|
|
582
716
|
this.forkSession.delete(binding);
|
|
@@ -587,8 +721,8 @@ export class OffscreenController {
|
|
|
587
721
|
}
|
|
588
722
|
releaseTurn(binding, lines) {
|
|
589
723
|
const changed = this.turnInProgress.delete(binding);
|
|
590
|
-
this.
|
|
591
|
-
this.
|
|
724
|
+
this.clearStagedAttemptTimers(binding);
|
|
725
|
+
this.pendingTurns.delete(binding);
|
|
592
726
|
this.clearTurnPoll(binding);
|
|
593
727
|
this.forkLocateWarned.delete(binding);
|
|
594
728
|
if (lines)
|
|
@@ -605,6 +739,17 @@ export class OffscreenController {
|
|
|
605
739
|
const abort = new AbortController();
|
|
606
740
|
this.forkLocateAborts.set(binding, abort);
|
|
607
741
|
const promise = (async () => {
|
|
742
|
+
const liveMonitor = fork.createTurnMonitor?.(this.forkMonitorOptions(binding));
|
|
743
|
+
if (liveMonitor) {
|
|
744
|
+
this.installForkTurnMonitor(binding, liveMonitor);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const policy = resolveProvider(fork.driver).forkSubmitFloor;
|
|
748
|
+
if (!policy) {
|
|
749
|
+
if (this.turnInProgress.has(binding))
|
|
750
|
+
this.warnForkSessionUnavailable(binding);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
608
753
|
await this.captureForkSubmitFloor(binding, fork, abort.signal);
|
|
609
754
|
if (this.disposed || abort.signal.aborted)
|
|
610
755
|
return;
|
|
@@ -617,10 +762,10 @@ export class OffscreenController {
|
|
|
617
762
|
return;
|
|
618
763
|
}
|
|
619
764
|
this.forkSessionFiles.set(binding, file);
|
|
620
|
-
if (!this.
|
|
765
|
+
if (!this.needsTurnObservation(binding)) {
|
|
621
766
|
try {
|
|
622
|
-
const lines = await
|
|
623
|
-
if (this.disposed || abort.signal.aborted || this.
|
|
767
|
+
const lines = await readTurnHistory(fork.driver, file);
|
|
768
|
+
if (this.disposed || abort.signal.aborted || this.needsTurnObservation(binding))
|
|
624
769
|
return;
|
|
625
770
|
this.refreshIdleBaseline(binding, lines);
|
|
626
771
|
}
|
|
@@ -629,29 +774,31 @@ export class OffscreenController {
|
|
|
629
774
|
}
|
|
630
775
|
}
|
|
631
776
|
else {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
777
|
+
while (!this.disposed && !abort.signal.aborted && this.needsTurnObservation(binding)) {
|
|
778
|
+
let lines;
|
|
779
|
+
try {
|
|
780
|
+
lines = await readTurnHistory(fork.driver, file);
|
|
781
|
+
}
|
|
782
|
+
catch {
|
|
783
|
+
if (this.turnInProgress.has(binding))
|
|
784
|
+
this.warnForkSessionUnavailable(binding);
|
|
785
|
+
await waitForRetry(this.turnPollMs, abort.signal);
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (this.disposed || abort.signal.aborted || !this.needsTurnObservation(binding))
|
|
642
789
|
return;
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
Math.max(0, submitCount(fork.driver, lines) + policy.offset));
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
790
|
+
if (policy.from === "fork-file" && !this.forkSubmitFloors.has(binding)) {
|
|
791
|
+
const providerFloor = forkFileSubmitFloor(fork.driver, fork.lineage, lines);
|
|
792
|
+
this.forkSubmitFloors.set(binding, providerFloor !== undefined
|
|
793
|
+
? 0
|
|
794
|
+
: Math.max(0, submitCount(fork.driver, lines) + policy.offset));
|
|
795
|
+
this.forkBaselines.set(binding, 0);
|
|
796
|
+
}
|
|
797
|
+
this.ensureTurnPoll(binding, lines);
|
|
651
798
|
return;
|
|
652
799
|
}
|
|
653
800
|
}
|
|
654
|
-
if (this.
|
|
801
|
+
if (this.needsTurnObservation(binding))
|
|
655
802
|
this.ensureTurnPoll(binding);
|
|
656
803
|
})().catch(() => {
|
|
657
804
|
// Fail closed: leave the binding busy when session discovery/read fails.
|
|
@@ -664,7 +811,8 @@ export class OffscreenController {
|
|
|
664
811
|
return promise;
|
|
665
812
|
}
|
|
666
813
|
async captureForkSubmitFloor(binding, fork, signal) {
|
|
667
|
-
|
|
814
|
+
const policy = resolveProvider(fork.driver).forkSubmitFloor;
|
|
815
|
+
if (!policy || policy.from !== "source")
|
|
668
816
|
return;
|
|
669
817
|
if (this.forkSubmitFloors.has(binding))
|
|
670
818
|
return;
|
|
@@ -682,16 +830,17 @@ export class OffscreenController {
|
|
|
682
830
|
const promise = (async () => {
|
|
683
831
|
while (!this.disposed && !signal.aborted && slot) {
|
|
684
832
|
try {
|
|
685
|
-
const lines = await
|
|
833
|
+
const lines = await readTurnHistory(fork.driver, slot.sourceSessionPath);
|
|
686
834
|
if (!this.disposed && !signal.aborted) {
|
|
687
835
|
this.forkSubmitFloors.set(binding, submitCount(fork.driver, lines));
|
|
688
836
|
}
|
|
689
837
|
return;
|
|
690
838
|
}
|
|
691
839
|
catch {
|
|
692
|
-
if (!this.
|
|
840
|
+
if (!this.needsTurnObservation(binding))
|
|
693
841
|
return;
|
|
694
|
-
this.
|
|
842
|
+
if (this.turnInProgress.has(binding))
|
|
843
|
+
this.warnForkSessionUnavailable(binding);
|
|
695
844
|
await waitForRetry(this.turnPollMs, signal);
|
|
696
845
|
}
|
|
697
846
|
}
|
|
@@ -706,8 +855,8 @@ export class OffscreenController {
|
|
|
706
855
|
refreshIdleBaseline(binding, lines) {
|
|
707
856
|
this.forkBaselines.set(binding, lines.length);
|
|
708
857
|
}
|
|
709
|
-
ensureTurnPoll(binding) {
|
|
710
|
-
if (!this.
|
|
858
|
+
ensureTurnPoll(binding, initialLines) {
|
|
859
|
+
if (!this.needsTurnObservation(binding))
|
|
711
860
|
return;
|
|
712
861
|
if (this.turnMonitors.has(binding))
|
|
713
862
|
return;
|
|
@@ -727,6 +876,8 @@ export class OffscreenController {
|
|
|
727
876
|
void this.ensureForkSession(binding, fork);
|
|
728
877
|
return;
|
|
729
878
|
}
|
|
879
|
+
if (!resolveProvider(fork.driver).forkSubmitFloor)
|
|
880
|
+
return;
|
|
730
881
|
const providerFork = resolveProvider(fork.driver).fork;
|
|
731
882
|
const effectiveSubmitCount = providerFork.forkFileSubmitFloor
|
|
732
883
|
? (lines) => Math.max(0, submitCount(fork.driver, lines) -
|
|
@@ -738,6 +889,14 @@ export class OffscreenController {
|
|
|
738
889
|
submitFloor: this.forkSubmitFloors.get(binding),
|
|
739
890
|
submitCount: effectiveSubmitCount,
|
|
740
891
|
readResult: readSessionFileWithStatus,
|
|
892
|
+
...this.forkMonitorOptions(binding),
|
|
893
|
+
});
|
|
894
|
+
this.installForkTurnMonitor(binding, monitor);
|
|
895
|
+
}
|
|
896
|
+
forkMonitorOptions(binding) {
|
|
897
|
+
return {
|
|
898
|
+
pollMs: this.turnPollMs,
|
|
899
|
+
attemptTimeoutMs: this.turnAttemptTimeoutMs,
|
|
741
900
|
onReadFailure: () => this.warnForkSessionUnavailable(binding),
|
|
742
901
|
onHistorySnapshot: (snapshot) => {
|
|
743
902
|
this.forkBaselines.set(binding, snapshot.lineCount);
|
|
@@ -747,35 +906,43 @@ export class OffscreenController {
|
|
|
747
906
|
this.host.scheduleParentUiRender();
|
|
748
907
|
},
|
|
749
908
|
onChange: (idle) => {
|
|
750
|
-
if (!idle)
|
|
909
|
+
if (!idle) {
|
|
910
|
+
if (!this.turnInProgress.has(binding)) {
|
|
911
|
+
this.turnInProgress.add(binding);
|
|
912
|
+
this.host.scheduleParentUiRender();
|
|
913
|
+
}
|
|
751
914
|
return;
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
if (
|
|
755
|
-
this.
|
|
915
|
+
}
|
|
916
|
+
const monitor = this.turnMonitors.get(binding);
|
|
917
|
+
if (monitor) {
|
|
918
|
+
this.forkBaselines.set(binding, monitor.lineBaseline);
|
|
919
|
+
const submitFloor = monitor.currentSubmitFloor;
|
|
920
|
+
if (submitFloor !== undefined) {
|
|
921
|
+
this.forkSubmitFloors.set(binding, submitFloor);
|
|
922
|
+
}
|
|
756
923
|
}
|
|
757
924
|
this.releaseTurn(binding);
|
|
758
925
|
},
|
|
759
|
-
}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
installForkTurnMonitor(binding, monitor, initialLines) {
|
|
760
929
|
this.turnMonitors.set(binding, monitor);
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const attempts = this.pendingTurnAttempts.get(binding) ?? [];
|
|
766
|
-
for (const attempt of attempts) {
|
|
767
|
-
monitor.beginTurnAttempt(attempt);
|
|
768
|
-
}
|
|
769
|
-
this.pendingTurnGenerations.delete(binding);
|
|
770
|
-
this.pendingTurnAttempts.delete(binding);
|
|
930
|
+
const pending = this.pendingTurns.get(binding) ?? [];
|
|
931
|
+
this.clearStagedAttemptTimers(binding);
|
|
932
|
+
this.pendingTurns.delete(binding);
|
|
933
|
+
monitor.attachTurns(pending, initialLines);
|
|
771
934
|
}
|
|
772
|
-
needsSubmitFloor(binding,
|
|
935
|
+
needsSubmitFloor(binding, fork) {
|
|
936
|
+
if (!resolveProvider(fork.driver).forkSubmitFloor)
|
|
937
|
+
return false;
|
|
773
938
|
return !this.forkSubmitFloors.has(binding);
|
|
774
939
|
}
|
|
775
940
|
clearTurnPoll(binding) {
|
|
776
941
|
const monitor = this.turnMonitors.get(binding);
|
|
777
942
|
if (monitor) {
|
|
778
|
-
if (monitor.supportsHistoryRegression
|
|
943
|
+
if (monitor.supportsHistoryRegression
|
|
944
|
+
|| monitor.hasUnobservedExpiredAttempts
|
|
945
|
+
|| monitor.classifyDeliverySince)
|
|
779
946
|
return;
|
|
780
947
|
monitor.dispose();
|
|
781
948
|
this.turnMonitors.delete(binding);
|
|
@@ -789,8 +956,10 @@ export class OffscreenController {
|
|
|
789
956
|
monitor.dispose();
|
|
790
957
|
this.turnMonitors.clear();
|
|
791
958
|
this.turnInProgress.clear();
|
|
792
|
-
this.
|
|
793
|
-
|
|
959
|
+
for (const timer of this.pendingAttemptTimers.values())
|
|
960
|
+
clearTimeout(timer);
|
|
961
|
+
this.pendingAttemptTimers.clear();
|
|
962
|
+
this.pendingTurns.clear();
|
|
794
963
|
this.forkSession.clear();
|
|
795
964
|
this.forkSessionFiles.clear();
|
|
796
965
|
this.forkBaselines.clear();
|
|
@@ -806,6 +975,7 @@ export class OffscreenController {
|
|
|
806
975
|
this.host.output.warn(`offscreen inquiry fork session unavailable for ${binding}; inquiry remains busy`);
|
|
807
976
|
}
|
|
808
977
|
abortIssueSend(binding) {
|
|
978
|
+
this.pendingQuestions.delete(binding);
|
|
809
979
|
const controller = this.sendControllers.get(binding);
|
|
810
980
|
if (controller) {
|
|
811
981
|
controller.abort(new Error("Inquiry send aborted"));
|
|
@@ -840,7 +1010,7 @@ export class OffscreenController {
|
|
|
840
1010
|
}
|
|
841
1011
|
updateVisibleMouseModes() {
|
|
842
1012
|
const fork = this.shown ? this.forks.get(this.shown) : undefined;
|
|
843
|
-
if (!
|
|
1013
|
+
if (!this.shown) {
|
|
844
1014
|
this.host.clearMouseModeSource(OFFSCREEN_MOUSE_SOURCE);
|
|
845
1015
|
return;
|
|
846
1016
|
}
|
|
@@ -852,7 +1022,7 @@ export class OffscreenController {
|
|
|
852
1022
|
this.updateVisibleMouseModes();
|
|
853
1023
|
}
|
|
854
1024
|
visibleMouseModes(fork) {
|
|
855
|
-
const modes = fork
|
|
1025
|
+
const modes = fork?.mouseModes() ?? [];
|
|
856
1026
|
const coordinator = this.activeCoordinator();
|
|
857
1027
|
if (!coordinator)
|
|
858
1028
|
return modes;
|
|
@@ -884,6 +1054,13 @@ function inquiryShortcutForKey(key) {
|
|
|
884
1054
|
function waitForRetry(ms, signal) {
|
|
885
1055
|
return abortableSleep(ms, signal, { onAbort: "resolve" });
|
|
886
1056
|
}
|
|
1057
|
+
async function readTurnHistory(driver, path) {
|
|
1058
|
+
const { lines, malformed } = await readSessionFileWithStatus(path);
|
|
1059
|
+
if (malformed || resolveProvider(driver).fork.submissionEvidence?.(lines).indeterminate) {
|
|
1060
|
+
throw new Error("Inquiry submission history is indeterminate");
|
|
1061
|
+
}
|
|
1062
|
+
return lines;
|
|
1063
|
+
}
|
|
887
1064
|
function isSubmitKey(key) {
|
|
888
1065
|
return key.kind === "enter"
|
|
889
1066
|
|| key.kind === "shift-enter"
|