@zaalipro/dsh-workflows 0.1.0-rc.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE.md +224 -0
- package/README.i18n.yaml +4 -0
- package/README.md +111 -0
- package/README.zh.md +111 -0
- package/cordis.patch.yml +10 -0
- package/docs/architecture.i18n.yaml +4 -0
- package/docs/architecture.md +177 -0
- package/docs/architecture.zh.md +177 -0
- package/docs/testing.i18n.yaml +4 -0
- package/docs/testing.md +125 -0
- package/docs/testing.zh.md +125 -0
- package/docs/user-guide.i18n.yaml +4 -0
- package/docs/user-guide.md +351 -0
- package/docs/user-guide.zh.md +351 -0
- package/lib/client-types/WorkflowMemberInspector.d.ts +28 -0
- package/lib/client-types/WorkflowMemberInspector.d.ts.map +1 -0
- package/lib/client-types/WorkflowMemberInspector.js +181 -0
- package/lib/client-types/WorkflowMemberInspector.js.map +1 -0
- package/lib/client-types/WorkflowRunPanel.d.ts +43 -0
- package/lib/client-types/WorkflowRunPanel.d.ts.map +1 -0
- package/lib/client-types/WorkflowRunPanel.js +130 -0
- package/lib/client-types/WorkflowRunPanel.js.map +1 -0
- package/lib/client-types/WorkflowsDashboard.d.ts +49 -0
- package/lib/client-types/WorkflowsDashboard.d.ts.map +1 -0
- package/lib/client-types/WorkflowsDashboard.js +942 -0
- package/lib/client-types/WorkflowsDashboard.js.map +1 -0
- package/lib/client-types/adapter.d.ts +60 -0
- package/lib/client-types/adapter.d.ts.map +1 -0
- package/lib/client-types/adapter.js +117 -0
- package/lib/client-types/adapter.js.map +1 -0
- package/lib/client-types/chat-renderer.d.ts +10 -0
- package/lib/client-types/chat-renderer.d.ts.map +1 -0
- package/lib/client-types/chat-renderer.js +11 -0
- package/lib/client-types/chat-renderer.js.map +1 -0
- package/lib/client-types/contract.d.ts +210 -0
- package/lib/client-types/contract.d.ts.map +1 -0
- package/lib/client-types/contract.js +37 -0
- package/lib/client-types/contract.js.map +1 -0
- package/lib/client-types/controller.d.ts +83 -0
- package/lib/client-types/controller.d.ts.map +1 -0
- package/lib/client-types/controller.js +705 -0
- package/lib/client-types/controller.js.map +1 -0
- package/lib/client-types/index.d.ts +19 -0
- package/lib/client-types/index.d.ts.map +1 -0
- package/lib/client-types/index.js +731 -0
- package/lib/client-types/index.js.map +1 -0
- package/lib/client-types/locales.d.ts +257 -0
- package/lib/client-types/locales.d.ts.map +1 -0
- package/lib/client-types/locales.js +270 -0
- package/lib/client-types/locales.js.map +1 -0
- package/lib/client-types/slot-components.d.ts +38 -0
- package/lib/client-types/slot-components.d.ts.map +1 -0
- package/lib/client-types/slot-components.js +22 -0
- package/lib/client-types/slot-components.js.map +1 -0
- package/lib/client-types/store.d.ts +69 -0
- package/lib/client-types/store.d.ts.map +1 -0
- package/lib/client-types/store.js +135 -0
- package/lib/client-types/store.js.map +1 -0
- package/lib/client-types/workflow-definition.d.ts +52 -0
- package/lib/client-types/workflow-definition.d.ts.map +1 -0
- package/lib/client-types/workflow-definition.js +135 -0
- package/lib/client-types/workflow-definition.js.map +1 -0
- package/lib/client.js +11065 -0
- package/lib/client.js.map +1 -0
- package/lib/compat-engine/index.js +1926 -0
- package/lib/compat-engine/index.js.map +1 -0
- package/lib/compat-engine/worker.cjs +1500 -0
- package/lib/compat-engine/worker.cjs.map +1 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +1709 -0
- package/lib/typert.remote-client.d.ts +57 -0
- package/lib/typert.remote-client.js +1662 -0
- package/lib/types/commands/aliases.d.ts +4 -0
- package/lib/types/commands/aliases.d.ts.map +1 -0
- package/lib/types/commands/aliases.js +25 -0
- package/lib/types/commands/aliases.js.map +1 -0
- package/lib/types/commands/index.d.ts +66 -0
- package/lib/types/commands/index.d.ts.map +1 -0
- package/lib/types/commands/index.js +550 -0
- package/lib/types/commands/index.js.map +1 -0
- package/lib/types/commands/parser.d.ts +17 -0
- package/lib/types/commands/parser.d.ts.map +1 -0
- package/lib/types/commands/parser.js +45 -0
- package/lib/types/commands/parser.js.map +1 -0
- package/lib/types/config.d.ts +71 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/config.js +109 -0
- package/lib/types/config.js.map +1 -0
- package/lib/types/index.d.ts +41 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +483 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/invariant.d.ts +21 -0
- package/lib/types/invariant.d.ts.map +1 -0
- package/lib/types/invariant.js +85 -0
- package/lib/types/invariant.js.map +1 -0
- package/lib/types/registry/definition.d.ts +24 -0
- package/lib/types/registry/definition.d.ts.map +1 -0
- package/lib/types/registry/definition.js +189 -0
- package/lib/types/registry/definition.js.map +1 -0
- package/lib/types/registry/index.d.ts +100 -0
- package/lib/types/registry/index.d.ts.map +1 -0
- package/lib/types/registry/index.js +899 -0
- package/lib/types/registry/index.js.map +1 -0
- package/lib/types/registry/names.d.ts +19 -0
- package/lib/types/registry/names.d.ts.map +1 -0
- package/lib/types/registry/names.js +69 -0
- package/lib/types/registry/names.js.map +1 -0
- package/lib/types/registry/remote.d.ts +15 -0
- package/lib/types/registry/remote.d.ts.map +1 -0
- package/lib/types/registry/remote.js +128 -0
- package/lib/types/registry/remote.js.map +1 -0
- package/lib/types/registry/roots.d.ts +44 -0
- package/lib/types/registry/roots.d.ts.map +1 -0
- package/lib/types/registry/roots.js +118 -0
- package/lib/types/registry/roots.js.map +1 -0
- package/lib/types/registry/types.d.ts +62 -0
- package/lib/types/registry/types.d.ts.map +1 -0
- package/lib/types/registry/types.js +2 -0
- package/lib/types/registry/types.js.map +1 -0
- package/lib/types/registry/watchers.d.ts +70 -0
- package/lib/types/registry/watchers.d.ts.map +1 -0
- package/lib/types/registry/watchers.js +152 -0
- package/lib/types/registry/watchers.js.map +1 -0
- package/lib/types/remote-events.d.ts +42 -0
- package/lib/types/remote-events.d.ts.map +1 -0
- package/lib/types/remote-events.js +21 -0
- package/lib/types/remote-events.js.map +1 -0
- package/lib/types/run-recorder.d.ts +44 -0
- package/lib/types/run-recorder.d.ts.map +1 -0
- package/lib/types/run-recorder.js +356 -0
- package/lib/types/run-recorder.js.map +1 -0
- package/lib/types/supervisor/canned-validate.d.ts +25 -0
- package/lib/types/supervisor/canned-validate.d.ts.map +1 -0
- package/lib/types/supervisor/canned-validate.js +122 -0
- package/lib/types/supervisor/canned-validate.js.map +1 -0
- package/lib/types/supervisor/completion-notice.d.ts +49 -0
- package/lib/types/supervisor/completion-notice.d.ts.map +1 -0
- package/lib/types/supervisor/completion-notice.js +281 -0
- package/lib/types/supervisor/completion-notice.js.map +1 -0
- package/lib/types/supervisor/cursors.d.ts +68 -0
- package/lib/types/supervisor/cursors.d.ts.map +1 -0
- package/lib/types/supervisor/cursors.js +158 -0
- package/lib/types/supervisor/cursors.js.map +1 -0
- package/lib/types/supervisor/engine-compat.d.ts +48 -0
- package/lib/types/supervisor/engine-compat.d.ts.map +1 -0
- package/lib/types/supervisor/engine-compat.js +116 -0
- package/lib/types/supervisor/engine-compat.js.map +1 -0
- package/lib/types/supervisor/index.d.ts +183 -0
- package/lib/types/supervisor/index.d.ts.map +1 -0
- package/lib/types/supervisor/index.js +2040 -0
- package/lib/types/supervisor/index.js.map +1 -0
- package/lib/types/supervisor/parallel-compat.d.ts +36 -0
- package/lib/types/supervisor/parallel-compat.d.ts.map +1 -0
- package/lib/types/supervisor/parallel-compat.js +180 -0
- package/lib/types/supervisor/parallel-compat.js.map +1 -0
- package/lib/types/supervisor/remote.d.ts +25 -0
- package/lib/types/supervisor/remote.d.ts.map +1 -0
- package/lib/types/supervisor/remote.js +478 -0
- package/lib/types/supervisor/remote.js.map +1 -0
- package/lib/types/supervisor/storage/bounded-file.d.ts +41 -0
- package/lib/types/supervisor/storage/bounded-file.d.ts.map +1 -0
- package/lib/types/supervisor/storage/bounded-file.js +186 -0
- package/lib/types/supervisor/storage/bounded-file.js.map +1 -0
- package/lib/types/supervisor/storage/details-codec.d.ts +14 -0
- package/lib/types/supervisor/storage/details-codec.d.ts.map +1 -0
- package/lib/types/supervisor/storage/details-codec.js +359 -0
- package/lib/types/supervisor/storage/details-codec.js.map +1 -0
- package/lib/types/supervisor/storage/index.d.ts +26 -0
- package/lib/types/supervisor/storage/index.d.ts.map +1 -0
- package/lib/types/supervisor/storage/index.js +100 -0
- package/lib/types/supervisor/storage/index.js.map +1 -0
- package/lib/types/supervisor/storage/lease.d.ts +34 -0
- package/lib/types/supervisor/storage/lease.d.ts.map +1 -0
- package/lib/types/supervisor/storage/lease.js +289 -0
- package/lib/types/supervisor/storage/lease.js.map +1 -0
- package/lib/types/supervisor/storage/manifest-codec.d.ts +10 -0
- package/lib/types/supervisor/storage/manifest-codec.d.ts.map +1 -0
- package/lib/types/supervisor/storage/manifest-codec.js +335 -0
- package/lib/types/supervisor/storage/manifest-codec.js.map +1 -0
- package/lib/types/supervisor/storage/manifest-store.d.ts +99 -0
- package/lib/types/supervisor/storage/manifest-store.d.ts.map +1 -0
- package/lib/types/supervisor/storage/manifest-store.js +1606 -0
- package/lib/types/supervisor/storage/manifest-store.js.map +1 -0
- package/lib/types/supervisor/storage/manifest-types.d.ts +269 -0
- package/lib/types/supervisor/storage/manifest-types.d.ts.map +1 -0
- package/lib/types/supervisor/storage/manifest-types.js +2 -0
- package/lib/types/supervisor/storage/manifest-types.js.map +1 -0
- package/lib/types/supervisor/storage/private-root.d.ts +143 -0
- package/lib/types/supervisor/storage/private-root.d.ts.map +1 -0
- package/lib/types/supervisor/storage/private-root.js +860 -0
- package/lib/types/supervisor/storage/private-root.js.map +1 -0
- package/lib/types/supervisor/storage/recovery.d.ts +14 -0
- package/lib/types/supervisor/storage/recovery.d.ts.map +1 -0
- package/lib/types/supervisor/storage/recovery.js +126 -0
- package/lib/types/supervisor/storage/recovery.js.map +1 -0
- package/lib/types/supervisor/storage/run-files.d.ts +67 -0
- package/lib/types/supervisor/storage/run-files.d.ts.map +1 -0
- package/lib/types/supervisor/storage/run-files.js +346 -0
- package/lib/types/supervisor/storage/run-files.js.map +1 -0
- package/lib/types/supervisor/types.d.ts +295 -0
- package/lib/types/supervisor/types.d.ts.map +1 -0
- package/lib/types/supervisor/types.js +3 -0
- package/lib/types/supervisor/types.js.map +1 -0
- package/lib/types/supervisor/value-view.d.ts +19 -0
- package/lib/types/supervisor/value-view.d.ts.map +1 -0
- package/lib/types/supervisor/value-view.js +183 -0
- package/lib/types/supervisor/value-view.js.map +1 -0
- package/lib/types/tool/index.d.ts +86 -0
- package/lib/types/tool/index.d.ts.map +1 -0
- package/lib/types/tool/index.js +810 -0
- package/lib/types/tool/index.js.map +1 -0
- package/lib/types/tool/schema.d.ts +158 -0
- package/lib/types/tool/schema.d.ts.map +1 -0
- package/lib/types/tool/schema.js +137 -0
- package/lib/types/tool/schema.js.map +1 -0
- package/lib/types/types.d.ts +9 -0
- package/lib/types/types.d.ts.map +1 -0
- package/lib/types/types.js +3 -0
- package/lib/types/types.js.map +1 -0
- package/lib/types/user-questions.d.ts +9 -0
- package/lib/types/user-questions.d.ts.map +1 -0
- package/lib/types/user-questions.js +91 -0
- package/lib/types/user-questions.js.map +1 -0
- package/package.json +291 -0
- package/skills/create-workflow/SKILL.md +166 -0
- package/vendor/workflow-engine/LICENSE +21 -0
- package/vendor/workflow-engine/README.md +1 -0
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
import { WorkflowRunsRemoteError, unwrapWorkflowRemoteResult, } from './contract.js';
|
|
2
|
+
const DEFAULT_LIMIT = 50;
|
|
3
|
+
const ABORT_NAME = 'AbortError';
|
|
4
|
+
/**
|
|
5
|
+
* Stock Harness does not expose the optional workflow invalidation event lane.
|
|
6
|
+
* Keep the currently observed dashboard Session live without allowing a slow
|
|
7
|
+
* request to build up an interval backlog.
|
|
8
|
+
*/
|
|
9
|
+
export const WORKFLOW_RUN_POLL_INTERVAL_MS = 2_000;
|
|
10
|
+
function abortError(reason = 'The operation was aborted') {
|
|
11
|
+
if (reason instanceof Error) {
|
|
12
|
+
if (reason.name !== ABORT_NAME)
|
|
13
|
+
reason.name = ABORT_NAME;
|
|
14
|
+
return reason;
|
|
15
|
+
}
|
|
16
|
+
const error = new Error(String(reason));
|
|
17
|
+
error.name = ABORT_NAME;
|
|
18
|
+
return error;
|
|
19
|
+
}
|
|
20
|
+
function isAbort(error) {
|
|
21
|
+
return (error instanceof Error && error.name === ABORT_NAME)
|
|
22
|
+
|| (typeof DOMException !== 'undefined' && error instanceof DOMException && error.name === ABORT_NAME);
|
|
23
|
+
}
|
|
24
|
+
function throwIfAborted(signal) {
|
|
25
|
+
if (signal?.aborted)
|
|
26
|
+
throw signal.reason ?? abortError();
|
|
27
|
+
}
|
|
28
|
+
/** Race a caller's wait without cancelling a shared baseline request. */
|
|
29
|
+
function waitWithAbort(promise, signal) {
|
|
30
|
+
if (signal === undefined)
|
|
31
|
+
return promise;
|
|
32
|
+
throwIfAborted(signal);
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
let settled = false;
|
|
35
|
+
const cleanup = () => signal.removeEventListener('abort', onAbort);
|
|
36
|
+
const onAbort = () => {
|
|
37
|
+
if (settled)
|
|
38
|
+
return;
|
|
39
|
+
settled = true;
|
|
40
|
+
cleanup();
|
|
41
|
+
reject(signal.reason ?? abortError());
|
|
42
|
+
};
|
|
43
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
44
|
+
void promise.then(value => {
|
|
45
|
+
if (settled)
|
|
46
|
+
return;
|
|
47
|
+
settled = true;
|
|
48
|
+
cleanup();
|
|
49
|
+
resolve(value);
|
|
50
|
+
}, error => {
|
|
51
|
+
if (settled)
|
|
52
|
+
return;
|
|
53
|
+
settled = true;
|
|
54
|
+
cleanup();
|
|
55
|
+
reject(error);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function combineSignals(left, right) {
|
|
60
|
+
if (left === undefined)
|
|
61
|
+
return right;
|
|
62
|
+
if (typeof AbortSignal.any === 'function')
|
|
63
|
+
return AbortSignal.any([left, right]);
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
const abort = () => controller.abort(left.reason ?? right.reason);
|
|
66
|
+
if (left.aborted || right.aborted)
|
|
67
|
+
abort();
|
|
68
|
+
else {
|
|
69
|
+
left.addEventListener('abort', abort, { once: true });
|
|
70
|
+
right.addEventListener('abort', abort, { once: true });
|
|
71
|
+
}
|
|
72
|
+
return controller.signal;
|
|
73
|
+
}
|
|
74
|
+
function emptySnapshot(sessionId, phase = 'idle') {
|
|
75
|
+
return {
|
|
76
|
+
sessionId,
|
|
77
|
+
phase,
|
|
78
|
+
status: phase,
|
|
79
|
+
runs: [],
|
|
80
|
+
total: 0,
|
|
81
|
+
sessionRevision: 0,
|
|
82
|
+
revision: 0,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function snapshotWith(sessionId, previous, patch) {
|
|
86
|
+
const phase = patch.phase ?? previous.phase;
|
|
87
|
+
const sessionRevision = patch.sessionRevision ?? patch.revision ?? previous.sessionRevision ?? previous.revision;
|
|
88
|
+
const nextCursor = Object.hasOwn(patch, 'nextCursor') ? patch.nextCursor : previous.nextCursor;
|
|
89
|
+
const epoch = Object.hasOwn(patch, 'epoch') ? patch.epoch : previous.epoch;
|
|
90
|
+
const error = Object.hasOwn(patch, 'error') ? patch.error : previous.error;
|
|
91
|
+
return {
|
|
92
|
+
sessionId,
|
|
93
|
+
phase,
|
|
94
|
+
status: phase,
|
|
95
|
+
runs: patch.runs ?? previous.runs,
|
|
96
|
+
total: patch.total ?? previous.total,
|
|
97
|
+
...(nextCursor === undefined ? {} : { nextCursor }),
|
|
98
|
+
...(epoch === undefined ? {} : { epoch }),
|
|
99
|
+
sessionRevision,
|
|
100
|
+
revision: sessionRevision,
|
|
101
|
+
...(error === undefined ? {} : { error }),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function renderThrown(error) {
|
|
105
|
+
try {
|
|
106
|
+
return error instanceof Error ? error.message : String(error);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return 'Unable to load workflow data. Retry.';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Lazy, revision-fenced browser source for retained workflow runs. */
|
|
113
|
+
export class WorkflowRunsController {
|
|
114
|
+
connection;
|
|
115
|
+
states = new Map();
|
|
116
|
+
/** Exact workflowRuns namespace. The client plugin replaces its empty
|
|
117
|
+
* pre-mount transport with root.get('remote.workflowRuns'). */
|
|
118
|
+
remoteNamespace;
|
|
119
|
+
agents;
|
|
120
|
+
connectionGeneration = 0;
|
|
121
|
+
connected = true;
|
|
122
|
+
observed;
|
|
123
|
+
disposed = false;
|
|
124
|
+
pollTimer;
|
|
125
|
+
pollGeneration = 0;
|
|
126
|
+
pollFlightGeneration;
|
|
127
|
+
constructor(remote, agents, connection) {
|
|
128
|
+
this.connection = connection;
|
|
129
|
+
this.remoteNamespace = remote;
|
|
130
|
+
this.agents = agents;
|
|
131
|
+
}
|
|
132
|
+
/** Replace the transport with the exact mounted workflowRuns namespace. */
|
|
133
|
+
setRemote(remote) {
|
|
134
|
+
this.remoteNamespace = remote;
|
|
135
|
+
}
|
|
136
|
+
/** Access the exact namespace; never traverse remote.workflowRuns through a
|
|
137
|
+
* traced aggregate (Cordis requires an explicit nested inject for that). */
|
|
138
|
+
get remote() {
|
|
139
|
+
return this.remoteNamespace;
|
|
140
|
+
}
|
|
141
|
+
state(sessionId) {
|
|
142
|
+
let state = this.states.get(sessionId);
|
|
143
|
+
if (state !== undefined)
|
|
144
|
+
return state;
|
|
145
|
+
state = {
|
|
146
|
+
sessionId,
|
|
147
|
+
snapshot: emptySnapshot(sessionId, this.connected ? 'idle' : 'reconnecting'),
|
|
148
|
+
listeners: new Set(),
|
|
149
|
+
requests: new Set(),
|
|
150
|
+
generation: this.connectionGeneration,
|
|
151
|
+
removed: false,
|
|
152
|
+
subscribed: false,
|
|
153
|
+
followup: false,
|
|
154
|
+
};
|
|
155
|
+
this.states.set(sessionId, state);
|
|
156
|
+
return state;
|
|
157
|
+
}
|
|
158
|
+
get(sessionId) {
|
|
159
|
+
return this.state(sessionId).snapshot;
|
|
160
|
+
}
|
|
161
|
+
source(sessionId) {
|
|
162
|
+
const state = this.state(sessionId);
|
|
163
|
+
if (state.source !== undefined)
|
|
164
|
+
return state.source;
|
|
165
|
+
state.source = {
|
|
166
|
+
getSnapshot: () => state.snapshot,
|
|
167
|
+
subscribe: (listener) => {
|
|
168
|
+
if (this.disposed || state.removed)
|
|
169
|
+
return () => undefined;
|
|
170
|
+
state.listeners.add(listener);
|
|
171
|
+
if (!state.subscribed) {
|
|
172
|
+
state.subscribed = true;
|
|
173
|
+
void this.refresh(sessionId).catch(() => undefined);
|
|
174
|
+
}
|
|
175
|
+
return () => {
|
|
176
|
+
state.listeners.delete(listener);
|
|
177
|
+
if (state.listeners.size === 0 && this.observed !== sessionId)
|
|
178
|
+
this.removeSession(sessionId);
|
|
179
|
+
};
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
return state.source;
|
|
183
|
+
}
|
|
184
|
+
subscribe(sessionId, listener) {
|
|
185
|
+
const source = this.source(sessionId);
|
|
186
|
+
const notify = () => listener(source.getSnapshot());
|
|
187
|
+
const unsubscribe = source.subscribe(notify);
|
|
188
|
+
try {
|
|
189
|
+
notify();
|
|
190
|
+
}
|
|
191
|
+
catch { /* a bad renderer must not starve other observers */ }
|
|
192
|
+
return unsubscribe;
|
|
193
|
+
}
|
|
194
|
+
observe(sessionId) {
|
|
195
|
+
if (this.observed === sessionId) {
|
|
196
|
+
if (sessionId !== undefined && !this.disposed) {
|
|
197
|
+
void this.refresh(sessionId).catch(() => undefined);
|
|
198
|
+
this.schedulePoll();
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.pollGeneration += 1;
|
|
203
|
+
this.clearPollTimer();
|
|
204
|
+
if (this.observed !== undefined) {
|
|
205
|
+
const previous = this.states.get(this.observed);
|
|
206
|
+
if (previous !== undefined && previous.listeners.size === 0)
|
|
207
|
+
this.removeSession(this.observed);
|
|
208
|
+
}
|
|
209
|
+
this.observed = sessionId;
|
|
210
|
+
if (sessionId === undefined || this.disposed)
|
|
211
|
+
return;
|
|
212
|
+
const state = this.state(sessionId);
|
|
213
|
+
state.subscribed = true;
|
|
214
|
+
void this.refresh(sessionId).catch(() => undefined);
|
|
215
|
+
this.schedulePoll();
|
|
216
|
+
}
|
|
217
|
+
clearPollTimer() {
|
|
218
|
+
if (this.pollTimer === undefined)
|
|
219
|
+
return;
|
|
220
|
+
clearTimeout(this.pollTimer);
|
|
221
|
+
this.pollTimer = undefined;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Chain polls after settlement rather than using setInterval: a hung or slow
|
|
225
|
+
* transport owns at most one poll wait, while refresh() still coalesces an
|
|
226
|
+
* event-driven invalidation landing during that wait.
|
|
227
|
+
*/
|
|
228
|
+
schedulePoll() {
|
|
229
|
+
const sessionId = this.observed;
|
|
230
|
+
const generation = this.pollGeneration;
|
|
231
|
+
if (sessionId === undefined || this.disposed || !this.connected
|
|
232
|
+
|| this.pollTimer !== undefined || this.pollFlightGeneration === generation)
|
|
233
|
+
return;
|
|
234
|
+
this.pollTimer = setTimeout(() => {
|
|
235
|
+
this.pollTimer = undefined;
|
|
236
|
+
if (this.disposed || !this.connected || this.observed !== sessionId
|
|
237
|
+
|| this.pollGeneration !== generation)
|
|
238
|
+
return;
|
|
239
|
+
this.pollFlightGeneration = generation;
|
|
240
|
+
void this.refreshWithMode(sessionId, undefined, true).catch(() => undefined).finally(() => {
|
|
241
|
+
if (this.pollFlightGeneration !== generation)
|
|
242
|
+
return;
|
|
243
|
+
this.pollFlightGeneration = undefined;
|
|
244
|
+
if (this.observed === sessionId && this.pollGeneration === generation)
|
|
245
|
+
this.schedulePoll();
|
|
246
|
+
});
|
|
247
|
+
}, WORKFLOW_RUN_POLL_INTERVAL_MS);
|
|
248
|
+
}
|
|
249
|
+
publish(state, patch) {
|
|
250
|
+
if (this.disposed || state.removed)
|
|
251
|
+
return;
|
|
252
|
+
state.snapshot = snapshotWith(state.sessionId, state.snapshot, patch);
|
|
253
|
+
for (const listener of [...state.listeners]) {
|
|
254
|
+
try {
|
|
255
|
+
listener();
|
|
256
|
+
}
|
|
257
|
+
catch { /* a bad renderer must not starve other observers */ }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
request(state, supplied) {
|
|
261
|
+
const controller = new AbortController();
|
|
262
|
+
state.requests.add(controller);
|
|
263
|
+
return {
|
|
264
|
+
signal: combineSignals(supplied, controller.signal),
|
|
265
|
+
controller,
|
|
266
|
+
generation: this.connectionGeneration,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
retire(state, controller) {
|
|
270
|
+
state.requests.delete(controller);
|
|
271
|
+
}
|
|
272
|
+
async call(method, sessionId, request, signal) {
|
|
273
|
+
const fn = this.remote?.[method];
|
|
274
|
+
const raw = typeof fn === 'function'
|
|
275
|
+
? await fn.call(this.remote, sessionId, request, signal)
|
|
276
|
+
: await this.callRpc(method, sessionId, request, signal);
|
|
277
|
+
signal.throwIfAborted();
|
|
278
|
+
return unwrapWorkflowRemoteResult(raw);
|
|
279
|
+
}
|
|
280
|
+
/** Stock may leave the typed stub unmounted; the Host still serves namespace/method over /api. */
|
|
281
|
+
async callRpc(method, sessionId, request, signal) {
|
|
282
|
+
const rpc = this.connection?.rpc?.call;
|
|
283
|
+
if (typeof rpc !== 'function') {
|
|
284
|
+
throw new WorkflowRunsRemoteError('storage-unavailable', `workflow Remote method ${method} is unavailable`);
|
|
285
|
+
}
|
|
286
|
+
return rpc.call(this.connection.rpc, '/api', `workflowRuns/${method}`, {
|
|
287
|
+
args: { agentId: sessionId, request },
|
|
288
|
+
}, signal);
|
|
289
|
+
}
|
|
290
|
+
async refresh(sessionId, supplied) {
|
|
291
|
+
try {
|
|
292
|
+
return await this.refreshWithMode(sessionId, supplied, false);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
// A foreground caller may have joined a background poll's single-flight.
|
|
296
|
+
// In that case the shared operation intentionally preserved `ready`, but
|
|
297
|
+
// the explicit Retry/launch refresh still owns an error surface.
|
|
298
|
+
const state = this.states.get(sessionId);
|
|
299
|
+
if (state !== undefined && state.snapshot.phase === 'ready' && !state.removed
|
|
300
|
+
&& !this.disposed && this.connected && !isAbort(error)) {
|
|
301
|
+
this.publish(state, {
|
|
302
|
+
phase: 'error',
|
|
303
|
+
runs: state.snapshot.runs,
|
|
304
|
+
total: state.snapshot.total,
|
|
305
|
+
error: renderThrown(error),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async refreshWithMode(sessionId, supplied, background) {
|
|
312
|
+
if (this.disposed)
|
|
313
|
+
return this.get(sessionId);
|
|
314
|
+
const state = this.state(sessionId);
|
|
315
|
+
if (state.removed || !this.connected)
|
|
316
|
+
return state.snapshot;
|
|
317
|
+
if (state.refreshFlight !== undefined)
|
|
318
|
+
return waitWithAbort(state.refreshFlight, supplied);
|
|
319
|
+
const request = this.request(state);
|
|
320
|
+
const generation = request.generation;
|
|
321
|
+
let operation;
|
|
322
|
+
operation = (async () => {
|
|
323
|
+
// A periodic fallback must not flash a healthy dashboard through loading
|
|
324
|
+
// every two seconds. Initial/manual reads retain their explicit phase.
|
|
325
|
+
if (!background || state.snapshot.phase !== 'ready') {
|
|
326
|
+
this.publish(state, { phase: 'loading', error: undefined });
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
const page = await this.call('list', sessionId, { limit: DEFAULT_LIMIT }, request.signal);
|
|
330
|
+
if (state.removed || this.disposed || generation !== this.connectionGeneration || request.signal.aborted)
|
|
331
|
+
return state.snapshot;
|
|
332
|
+
const items = Array.isArray(page?.items) ? page.items : [];
|
|
333
|
+
const sessionRevision = Number.isSafeInteger(page?.sessionRevision) ? page.sessionRevision : 0;
|
|
334
|
+
this.publish(state, {
|
|
335
|
+
phase: 'ready',
|
|
336
|
+
runs: items,
|
|
337
|
+
total: Number.isSafeInteger(page?.total) ? page.total : items.length,
|
|
338
|
+
nextCursor: page?.nextCursor,
|
|
339
|
+
epoch: typeof page?.epoch === 'string' ? page.epoch : undefined,
|
|
340
|
+
sessionRevision,
|
|
341
|
+
revision: sessionRevision,
|
|
342
|
+
error: undefined,
|
|
343
|
+
});
|
|
344
|
+
const hinted = state.hintedRevision;
|
|
345
|
+
state.hintedRevision = undefined;
|
|
346
|
+
if (hinted !== undefined && hinted > sessionRevision)
|
|
347
|
+
state.followup = true;
|
|
348
|
+
return state.snapshot;
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
if (isAbort(error) || request.signal.aborted || generation !== this.connectionGeneration)
|
|
352
|
+
return state.snapshot;
|
|
353
|
+
// Preserve the last authoritative ready snapshot across a transient
|
|
354
|
+
// polling failure. Connection lifecycle signals own the reconnecting
|
|
355
|
+
// phase; an explicit Retry still surfaces its error normally.
|
|
356
|
+
if (!background || state.snapshot.phase !== 'ready') {
|
|
357
|
+
this.publish(state, {
|
|
358
|
+
phase: 'error',
|
|
359
|
+
runs: state.snapshot.runs,
|
|
360
|
+
total: state.snapshot.total,
|
|
361
|
+
error: renderThrown(error),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
finally {
|
|
367
|
+
this.retire(state, request.controller);
|
|
368
|
+
// A disconnect/reset can retire this flight and start a new baseline
|
|
369
|
+
// before this `finally` runs. Never clear the replacement flight.
|
|
370
|
+
if (state.refreshFlight === operation)
|
|
371
|
+
state.refreshFlight = undefined;
|
|
372
|
+
if (state.followup && this.connected && !state.removed && !this.disposed) {
|
|
373
|
+
state.followup = false;
|
|
374
|
+
queueMicrotask(() => {
|
|
375
|
+
if (state.refreshFlight === undefined && (state.listeners.size > 0 || this.observed === sessionId)) {
|
|
376
|
+
void this.refresh(sessionId).catch(() => undefined);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
})();
|
|
382
|
+
state.refreshFlight = operation;
|
|
383
|
+
return waitWithAbort(operation, supplied);
|
|
384
|
+
}
|
|
385
|
+
async loadMore(sessionId, supplied) {
|
|
386
|
+
const state = this.state(sessionId);
|
|
387
|
+
const cursor = state.snapshot.nextCursor;
|
|
388
|
+
if (state.removed || this.disposed || cursor === undefined || !this.connected)
|
|
389
|
+
return state.snapshot;
|
|
390
|
+
if (state.pageFlight !== undefined)
|
|
391
|
+
return waitWithAbort(state.pageFlight, supplied);
|
|
392
|
+
// A later-page failure deliberately leaves the successful prefix and its
|
|
393
|
+
// cursor ready. The caller owns the inline paging error; turning the whole
|
|
394
|
+
// source into `error` would create a second dashboard alert for one fault.
|
|
395
|
+
const request = this.request(state, supplied);
|
|
396
|
+
const generation = request.generation;
|
|
397
|
+
const expectedEpoch = state.snapshot.epoch;
|
|
398
|
+
const expectedRevision = state.snapshot.sessionRevision;
|
|
399
|
+
let operation;
|
|
400
|
+
operation = (async () => {
|
|
401
|
+
try {
|
|
402
|
+
const page = await this.call('list', sessionId, { cursor, limit: DEFAULT_LIMIT }, request.signal);
|
|
403
|
+
if (state.removed || this.disposed || generation !== this.connectionGeneration || request.signal.aborted)
|
|
404
|
+
return state.snapshot;
|
|
405
|
+
if ((expectedEpoch !== undefined && page?.epoch !== undefined && page.epoch !== expectedEpoch)
|
|
406
|
+
|| (page?.sessionRevision !== undefined && page.sessionRevision !== expectedRevision)) {
|
|
407
|
+
await this.refresh(sessionId);
|
|
408
|
+
return state.snapshot;
|
|
409
|
+
}
|
|
410
|
+
const incoming = Array.isArray(page?.items) ? page.items : [];
|
|
411
|
+
const known = new Set(state.snapshot.runs.map(run => run.runId));
|
|
412
|
+
const rows = [...state.snapshot.runs];
|
|
413
|
+
for (const row of incoming) {
|
|
414
|
+
if (known.has(row.runId)) {
|
|
415
|
+
const index = rows.findIndex(existing => existing.runId === row.runId);
|
|
416
|
+
if (index >= 0 && row.revision > rows[index].revision)
|
|
417
|
+
rows[index] = row;
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
known.add(row.runId);
|
|
421
|
+
rows.push(row);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
this.publish(state, {
|
|
425
|
+
phase: 'ready',
|
|
426
|
+
runs: rows,
|
|
427
|
+
total: Number.isSafeInteger(page?.total) ? page.total : Math.max(state.snapshot.total, rows.length),
|
|
428
|
+
nextCursor: page?.nextCursor,
|
|
429
|
+
sessionRevision: Number.isSafeInteger(page?.sessionRevision) ? page.sessionRevision : expectedRevision,
|
|
430
|
+
revision: Number.isSafeInteger(page?.sessionRevision) ? page.sessionRevision : expectedRevision,
|
|
431
|
+
error: undefined,
|
|
432
|
+
});
|
|
433
|
+
return state.snapshot;
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
if (isAbort(error) || request.signal.aborted || generation !== this.connectionGeneration)
|
|
437
|
+
return state.snapshot;
|
|
438
|
+
// A live collection commonly advances while the user has an older
|
|
439
|
+
// continuation token. Recover with a fresh first page instead of
|
|
440
|
+
// surfacing a transient stale-cursor alert that Retry would merely
|
|
441
|
+
// turn into this same refresh.
|
|
442
|
+
if (error instanceof WorkflowRunsRemoteError && error.code === 'stale-cursor') {
|
|
443
|
+
return this.refresh(sessionId, supplied);
|
|
444
|
+
}
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
finally {
|
|
448
|
+
this.retire(state, request.controller);
|
|
449
|
+
if (state.pageFlight === operation)
|
|
450
|
+
state.pageFlight = undefined;
|
|
451
|
+
}
|
|
452
|
+
})();
|
|
453
|
+
state.pageFlight = operation;
|
|
454
|
+
return waitWithAbort(operation, supplied);
|
|
455
|
+
}
|
|
456
|
+
async read(sessionId, method, requestBody, supplied) {
|
|
457
|
+
const state = this.state(sessionId);
|
|
458
|
+
if (state.removed || this.disposed)
|
|
459
|
+
throw abortError('workflow Session was removed');
|
|
460
|
+
const request = this.request(state, supplied);
|
|
461
|
+
try {
|
|
462
|
+
const value = await this.call(method, sessionId, requestBody, request.signal);
|
|
463
|
+
if (state.removed || this.disposed || request.generation !== this.connectionGeneration) {
|
|
464
|
+
throw abortError('workflow request was superseded');
|
|
465
|
+
}
|
|
466
|
+
return value;
|
|
467
|
+
}
|
|
468
|
+
finally {
|
|
469
|
+
this.retire(state, request.controller);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
detail(sessionId, runId, signal) {
|
|
473
|
+
return this.read(sessionId, 'detail', { runId }, signal);
|
|
474
|
+
}
|
|
475
|
+
members(sessionId, runId, cursor, signal) {
|
|
476
|
+
return this.read(sessionId, 'members', { runId, ...(cursor === undefined ? {} : { cursor }), limit: DEFAULT_LIMIT }, signal);
|
|
477
|
+
}
|
|
478
|
+
memberDetail(sessionId, runId, memberId, signal) {
|
|
479
|
+
return this.read(sessionId, 'memberDetail', { runId, memberId }, signal);
|
|
480
|
+
}
|
|
481
|
+
logs(sessionId, runId, cursor, signal) {
|
|
482
|
+
return this.read(sessionId, 'logs', { runId, ...(cursor === undefined ? {} : { cursor }), limit: DEFAULT_LIMIT }, signal);
|
|
483
|
+
}
|
|
484
|
+
result(sessionId, runId, signal) {
|
|
485
|
+
return this.read(sessionId, 'result', { runId }, signal);
|
|
486
|
+
}
|
|
487
|
+
artifacts(sessionId, runId, cursor, signal) {
|
|
488
|
+
return this.read(sessionId, 'artifacts', { runId, ...(cursor === undefined ? {} : { cursor }), limit: DEFAULT_LIMIT }, signal);
|
|
489
|
+
}
|
|
490
|
+
artifact(sessionId, runId, name, cursor, expectedRevision, signal) {
|
|
491
|
+
return this.read(sessionId, 'artifact', {
|
|
492
|
+
runId,
|
|
493
|
+
name,
|
|
494
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
495
|
+
...(expectedRevision === undefined ? {} : { expectedRevision }),
|
|
496
|
+
}, signal);
|
|
497
|
+
}
|
|
498
|
+
async control(sessionId, runId, action, expectedRevision, signal) {
|
|
499
|
+
const state = this.state(sessionId);
|
|
500
|
+
const beforeGeneration = this.connectionGeneration;
|
|
501
|
+
try {
|
|
502
|
+
const result = await this.read(sessionId, 'control', { runId, action, expectedRevision }, signal);
|
|
503
|
+
if (beforeGeneration !== this.connectionGeneration || state.removed || this.disposed)
|
|
504
|
+
return result;
|
|
505
|
+
const row = result?.run;
|
|
506
|
+
if (row === undefined)
|
|
507
|
+
return result;
|
|
508
|
+
const index = state.snapshot.runs.findIndex(candidate => candidate.runId === row.runId);
|
|
509
|
+
if (index >= 0 && row.revision >= state.snapshot.runs[index].revision) {
|
|
510
|
+
const runs = state.snapshot.runs.map((candidate, i) => i === index ? row : candidate);
|
|
511
|
+
this.publish(state, { phase: 'ready', runs, error: undefined });
|
|
512
|
+
}
|
|
513
|
+
if (state.snapshot.nextCursor !== undefined && state.listeners.size > 0 && this.connected) {
|
|
514
|
+
void this.refresh(sessionId).catch(() => undefined);
|
|
515
|
+
}
|
|
516
|
+
return result;
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
if (error instanceof WorkflowRunsRemoteError && error.code === 'revision-conflict') {
|
|
520
|
+
const authoritative = error.details?.run;
|
|
521
|
+
if (authoritative !== undefined) {
|
|
522
|
+
const runs = state.snapshot.runs.map(row => row.runId === authoritative.runId ? authoritative : row);
|
|
523
|
+
this.publish(state, { runs });
|
|
524
|
+
}
|
|
525
|
+
void this.refresh(sessionId).catch(() => undefined);
|
|
526
|
+
}
|
|
527
|
+
throw error;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
handleChange(change) {
|
|
531
|
+
if (this.disposed)
|
|
532
|
+
return;
|
|
533
|
+
if (change.kind === 'invalidate-all') {
|
|
534
|
+
for (const state of this.states.values()) {
|
|
535
|
+
if (state.removed || !state.subscribed)
|
|
536
|
+
continue;
|
|
537
|
+
if (state.refreshFlight !== undefined)
|
|
538
|
+
state.followup = true;
|
|
539
|
+
else if (this.connected)
|
|
540
|
+
void this.refresh(state.sessionId).catch(() => undefined);
|
|
541
|
+
else
|
|
542
|
+
state.followup = true;
|
|
543
|
+
}
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const state = this.states.get(change.sessionId);
|
|
547
|
+
if (state === undefined || state.removed || !state.subscribed)
|
|
548
|
+
return;
|
|
549
|
+
const current = state.snapshot.sessionRevision ?? state.snapshot.revision;
|
|
550
|
+
if (change.revision <= current)
|
|
551
|
+
return;
|
|
552
|
+
state.hintedRevision = Math.max(state.hintedRevision ?? 0, change.revision);
|
|
553
|
+
if (state.refreshFlight !== undefined) {
|
|
554
|
+
state.followup = true;
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (this.connected)
|
|
558
|
+
void this.refresh(change.sessionId).catch(() => undefined);
|
|
559
|
+
else
|
|
560
|
+
state.followup = true;
|
|
561
|
+
}
|
|
562
|
+
handleDisconnected() {
|
|
563
|
+
this.connected = false;
|
|
564
|
+
this.connectionGeneration += 1;
|
|
565
|
+
this.pollGeneration += 1;
|
|
566
|
+
this.clearPollTimer();
|
|
567
|
+
for (const state of this.states.values()) {
|
|
568
|
+
if (state.removed)
|
|
569
|
+
continue;
|
|
570
|
+
state.generation = this.connectionGeneration;
|
|
571
|
+
for (const controller of state.requests)
|
|
572
|
+
controller.abort(abortError('workflow connection disconnected'));
|
|
573
|
+
state.requests.clear();
|
|
574
|
+
state.refreshFlight = undefined;
|
|
575
|
+
state.pageFlight = undefined;
|
|
576
|
+
this.publish(state, { phase: 'reconnecting' });
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/** Fence an explicit connection/reset even when description loss was not observed. */
|
|
580
|
+
handleReset() {
|
|
581
|
+
if (this.disposed)
|
|
582
|
+
return;
|
|
583
|
+
// Restore connected so a reset after description-loss actually refetches
|
|
584
|
+
// the epoch baseline instead of no-op'ing inside refresh().
|
|
585
|
+
this.connected = true;
|
|
586
|
+
this.connectionGeneration += 1;
|
|
587
|
+
this.pollGeneration += 1;
|
|
588
|
+
this.clearPollTimer();
|
|
589
|
+
for (const state of this.states.values()) {
|
|
590
|
+
if (state.removed)
|
|
591
|
+
continue;
|
|
592
|
+
state.generation = this.connectionGeneration;
|
|
593
|
+
for (const controller of state.requests)
|
|
594
|
+
controller.abort(abortError('workflow connection reset'));
|
|
595
|
+
state.requests.clear();
|
|
596
|
+
state.refreshFlight = undefined;
|
|
597
|
+
state.pageFlight = undefined;
|
|
598
|
+
state.hintedRevision = undefined;
|
|
599
|
+
state.followup = false;
|
|
600
|
+
if (state.subscribed && (state.listeners.size > 0 || this.observed === state.sessionId)) {
|
|
601
|
+
void this.refresh(state.sessionId).catch(() => undefined);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
this.schedulePoll();
|
|
605
|
+
}
|
|
606
|
+
handleConnected() {
|
|
607
|
+
if (this.disposed)
|
|
608
|
+
return;
|
|
609
|
+
this.connected = true;
|
|
610
|
+
this.pollGeneration += 1;
|
|
611
|
+
this.clearPollTimer();
|
|
612
|
+
for (const state of this.states.values()) {
|
|
613
|
+
if (state.removed || !state.subscribed)
|
|
614
|
+
continue;
|
|
615
|
+
state.hintedRevision = undefined;
|
|
616
|
+
state.followup = false;
|
|
617
|
+
if (state.listeners.size > 0 || this.observed === state.sessionId)
|
|
618
|
+
void this.refresh(state.sessionId).catch(() => undefined);
|
|
619
|
+
}
|
|
620
|
+
this.schedulePoll();
|
|
621
|
+
}
|
|
622
|
+
removeSession(sessionId) {
|
|
623
|
+
const state = this.states.get(sessionId);
|
|
624
|
+
if (state === undefined)
|
|
625
|
+
return;
|
|
626
|
+
state.removed = true;
|
|
627
|
+
state.generation += 1;
|
|
628
|
+
for (const controller of state.requests)
|
|
629
|
+
controller.abort(abortError('workflow Session was removed'));
|
|
630
|
+
state.requests.clear();
|
|
631
|
+
state.refreshFlight = undefined;
|
|
632
|
+
state.pageFlight = undefined;
|
|
633
|
+
state.snapshot = emptySnapshot(sessionId);
|
|
634
|
+
for (const listener of [...state.listeners]) {
|
|
635
|
+
try {
|
|
636
|
+
listener();
|
|
637
|
+
}
|
|
638
|
+
catch { /* contain observer failures */ }
|
|
639
|
+
}
|
|
640
|
+
state.listeners.clear();
|
|
641
|
+
this.states.delete(sessionId);
|
|
642
|
+
if (this.observed === sessionId) {
|
|
643
|
+
this.observed = undefined;
|
|
644
|
+
this.pollGeneration += 1;
|
|
645
|
+
this.clearPollTimer();
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
async resolveAndOpenChild(parentSessionId, childSessionId) {
|
|
649
|
+
try {
|
|
650
|
+
const sessions = this.agents?.sessions ?? this.agents;
|
|
651
|
+
if (sessions === undefined)
|
|
652
|
+
return false;
|
|
653
|
+
await sessions.refreshSubagents?.(parentSessionId);
|
|
654
|
+
const snapshot = sessions.list?.getSnapshot?.() ?? this.agents?.list?.getSnapshot?.();
|
|
655
|
+
const byParent = snapshot?.subagentsByParent?.[parentSessionId];
|
|
656
|
+
if (byParent?.state !== 'ready')
|
|
657
|
+
return false;
|
|
658
|
+
const entry = byParent.entries?.find((candidate) => {
|
|
659
|
+
const id = candidate?.id ?? candidate?.childSessionId;
|
|
660
|
+
const parent = candidate?.parentSessionId ?? candidate?.parentId;
|
|
661
|
+
return candidate?.kind === 'child'
|
|
662
|
+
&& candidate?.mode === 'one-shot'
|
|
663
|
+
&& id === childSessionId
|
|
664
|
+
&& (parent === undefined || parent === parentSessionId);
|
|
665
|
+
});
|
|
666
|
+
if (entry === undefined)
|
|
667
|
+
return false;
|
|
668
|
+
const address = { parentSessionId, childSessionId, mode: 'one-shot' };
|
|
669
|
+
if (typeof sessions.openSubagent === 'function') {
|
|
670
|
+
sessions.openSubagent(address);
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
if (typeof this.agents?.openSubagent === 'function') {
|
|
674
|
+
this.agents.openSubagent(address);
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
catch {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
dispose() {
|
|
684
|
+
if (this.disposed)
|
|
685
|
+
return;
|
|
686
|
+
this.disposed = true;
|
|
687
|
+
this.pollGeneration += 1;
|
|
688
|
+
this.clearPollTimer();
|
|
689
|
+
this.connectionGeneration += 1;
|
|
690
|
+
for (const state of this.states.values()) {
|
|
691
|
+
state.removed = true;
|
|
692
|
+
for (const controller of state.requests)
|
|
693
|
+
controller.abort(abortError('workflow controller disposed'));
|
|
694
|
+
state.requests.clear();
|
|
695
|
+
state.listeners.clear();
|
|
696
|
+
}
|
|
697
|
+
this.states.clear();
|
|
698
|
+
this.observed = undefined;
|
|
699
|
+
}
|
|
700
|
+
/** Compatibility names used by early package consumers. */
|
|
701
|
+
invalidate(change) { this.handleChange(change); }
|
|
702
|
+
reconnecting() { this.handleDisconnected(); }
|
|
703
|
+
}
|
|
704
|
+
export default WorkflowRunsController;
|
|
705
|
+
//# sourceMappingURL=controller.js.map
|