@tt-a1i/hive 2.1.6 → 2.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/src/server/agent-runtime-contract.d.ts +1 -9
- package/dist/src/server/agent-runtime.js +1 -7
- package/dist/src/server/agent-stdin-dispatcher.d.ts +2 -11
- package/dist/src/server/agent-stdin-dispatcher.js +14 -12
- package/dist/src/server/post-start-input-writer.js +1 -1
- package/dist/src/server/report-outbox-store.d.ts +10 -8
- package/dist/src/server/report-outbox-store.js +10 -1
- package/dist/src/server/routes-remote.js +46 -14
- package/dist/src/server/routes-team.js +1 -1
- package/dist/src/server/routes-workspaces.js +3 -6
- package/dist/src/server/runtime-store-contract.d.ts +2 -2
- package/dist/src/server/runtime-store-helpers.d.ts +4 -0
- package/dist/src/server/runtime-store-helpers.js +34 -11
- package/dist/src/server/runtime-store-worker-mutations.js +12 -6
- package/dist/src/server/runtime-store-workflows.js +7 -1
- package/dist/src/server/team-operations.d.ts +12 -5
- package/dist/src/server/team-operations.js +217 -158
- package/dist/src/shared/remote-bridge-routing.js +3 -1
- package/package.json +1 -1
- package/web/dist/assets/{AddWorkerDialog-DFNkbR_Q.js → AddWorkerDialog-DuUFz6UK.js} +2 -2
- package/web/dist/assets/{AddWorkspaceFlow-BjpjCuRe.js → AddWorkspaceFlow-CFuHJXFe.js} +1 -1
- package/web/dist/assets/{FirstRunWizard-BuLgUAny.js → FirstRunWizard-qA5b90Ru.js} +1 -1
- package/web/dist/assets/{MarketplaceDrawer-BALuhrnd.js → MarketplaceDrawer-BdkVJyI0.js} +1 -1
- package/web/dist/assets/{TaskGraphDrawer-DQTS6JPI.js → TaskGraphDrawer-DDhX2p_z.js} +1 -1
- package/web/dist/assets/{WhatsNewDialog-DpgjEIaA.js → WhatsNewDialog-Cp_gv_Co.js} +1 -1
- package/web/dist/assets/{WorkerModal-1y-Xdwv-.js → WorkerModal-BbKQsSSJ.js} +1 -1
- package/web/dist/assets/{WorkflowsDrawer-DPWlZs4S.js → WorkflowsDrawer-wk6HSGNl.js} +1 -1
- package/web/dist/assets/{WorkspaceMemoryDrawer-C0vny4zK.js → WorkspaceMemoryDrawer-CSxU_0b1.js} +1 -1
- package/web/dist/assets/{WorkspaceTaskDrawer-CJcFZh8C.js → WorkspaceTaskDrawer-Bz3fMad9.js} +1 -1
- package/web/dist/assets/{index-CR1SPGBZ.js → index-B3XoRj0C.js} +28 -28
- package/web/dist/assets/{search-Bl5O-SfR.js → search-BdM5IXYB.js} +1 -1
- package/web/dist/assets/{square-terminal-DCDaflg3.js → square-terminal-DnmshYpr.js} +1 -1
- package/web/dist/index.html +1 -1
- package/web/dist/sw.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-facing changes will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 2.1.7 - 2026-06-23
|
|
6
|
+
|
|
7
|
+
Team delivery recovery and remote-access hardening.
|
|
8
|
+
|
|
9
|
+
- Makes worker reports durable when the Orchestrator is offline or restarting,
|
|
10
|
+
so completed work is queued for redelivery instead of being silently lost.
|
|
11
|
+
- Tightens cancel, worker-dismiss, and queued-dispatch recovery paths so stale
|
|
12
|
+
work gets a clear dropped/cancelled outcome instead of leaving agents waiting.
|
|
13
|
+
- Makes user input delivery truthful: if the Orchestrator terminal is offline,
|
|
14
|
+
Hive rejects the input instead of recording a message that was never sent.
|
|
15
|
+
- Standardizes remote-access HTTP responses on snake_case fields and keeps the
|
|
16
|
+
web UI mapping them back into its internal camelCase model.
|
|
17
|
+
- Blocks remote devices from reading or approving desktop-only pairing
|
|
18
|
+
approvals through the tunnel, while preserving normal equal-authority device
|
|
19
|
+
management routes.
|
|
20
|
+
|
|
5
21
|
## 2.1.6 - 2026-06-21
|
|
6
22
|
|
|
7
23
|
Team UX polish and release visibility.
|
|
@@ -23,26 +23,18 @@ export interface AgentRuntime {
|
|
|
23
23
|
startAgent: (workspace: WorkspaceSummary, agentId: string, input: StartAgentOptions) => Promise<LiveAgentRun>;
|
|
24
24
|
stopAgentRun: (runId: string) => void;
|
|
25
25
|
validateAgentToken: AgentTokenRegistry['validate'];
|
|
26
|
-
writeReportPrompt: (workspaceId: string, workerName: string, workerId: string, text: string, artifacts: string[], input?: {
|
|
27
|
-
requireActiveRun?: boolean;
|
|
28
|
-
}) => void;
|
|
29
26
|
writeStatusPrompt: (workspaceId: string, workerName: string, workerId: string, text: string, artifacts: string[], input?: {
|
|
30
27
|
requireActiveRun?: boolean;
|
|
31
28
|
}) => void;
|
|
32
29
|
writeSendPrompt: (workspaceId: string, workerId: string, dispatchId: string, fromAgentName: string, workerDescription: string, text: string) => Promise<void>;
|
|
33
30
|
writeCancelPrompt: (workspaceId: string, workerId: string, dispatchId: string, reason: string, input?: {
|
|
34
31
|
requireActiveRun?: boolean;
|
|
35
|
-
}) => void
|
|
32
|
+
}) => Promise<void>;
|
|
36
33
|
writeUserInputPrompt: (workspaceId: string, text: string) => void;
|
|
37
34
|
writeSystemMessageToAgent: (workspaceId: string, agentId: string, text: string) => void;
|
|
38
35
|
deliverUserInputToOrchestrator: (workspaceId: string, text: string, input?: {
|
|
39
36
|
requireActiveRun?: boolean;
|
|
40
37
|
}) => Promise<void>;
|
|
41
|
-
/** Awaitable report delivery — rejects if the orchestrator PTY write fails,
|
|
42
|
-
* so the caller can persist the report to the redelivery outbox. */
|
|
43
|
-
deliverReportToOrchestrator: (workspaceId: string, workerName: string, text: string, artifacts: string[], input?: {
|
|
44
|
-
requireActiveRun?: boolean;
|
|
45
|
-
}) => Promise<void>;
|
|
46
38
|
/** Awaitable opaque delivery — used to drain the report outbox so an entry
|
|
47
39
|
* is marked delivered only after the PTY write resolves. */
|
|
48
40
|
deliverSystemMessageToAgent: (workspaceId: string, agentId: string, text: string, input?: {
|
|
@@ -139,9 +139,6 @@ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, ge
|
|
|
139
139
|
});
|
|
140
140
|
},
|
|
141
141
|
validateAgentToken: tokenRegistry.validate,
|
|
142
|
-
writeReportPrompt(workspaceId, workerName, _workerId, text, artifacts, input = {}) {
|
|
143
|
-
stdinDispatcher.writeReportPrompt(workspaceId, workerName, text, artifacts, input);
|
|
144
|
-
},
|
|
145
142
|
writeStatusPrompt(workspaceId, workerName, _workerId, text, artifacts, input = {}) {
|
|
146
143
|
stdinDispatcher.writeStatusPrompt(workspaceId, workerName, text, artifacts, input);
|
|
147
144
|
},
|
|
@@ -149,7 +146,7 @@ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, ge
|
|
|
149
146
|
return stdinDispatcher.writeSendPrompt(workspaceId, workerId, dispatchId, fromAgentName, workerDescription, text);
|
|
150
147
|
},
|
|
151
148
|
writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input = {}) {
|
|
152
|
-
stdinDispatcher.writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input);
|
|
149
|
+
return stdinDispatcher.writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input);
|
|
153
150
|
},
|
|
154
151
|
writeUserInputPrompt(workspaceId, text) {
|
|
155
152
|
stdinDispatcher.writeUserInputPrompt(workspaceId, text);
|
|
@@ -160,9 +157,6 @@ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, ge
|
|
|
160
157
|
deliverUserInputToOrchestrator(workspaceId, text, input = {}) {
|
|
161
158
|
return stdinDispatcher.deliverUserInputToOrchestrator(workspaceId, text, input);
|
|
162
159
|
},
|
|
163
|
-
deliverReportToOrchestrator(workspaceId, workerName, text, artifacts, input = {}) {
|
|
164
|
-
return stdinDispatcher.deliverReportToOrchestrator(workspaceId, workerName, text, artifacts, input);
|
|
165
|
-
},
|
|
166
160
|
deliverSystemMessageToAgent(workspaceId, agentId, text, input = {}) {
|
|
167
161
|
return stdinDispatcher.deliverSystemMessageToAgent(workspaceId, agentId, text, input);
|
|
168
162
|
},
|
|
@@ -16,22 +16,19 @@ interface AgentStdinDispatcherInput {
|
|
|
16
16
|
* Optional; omitted → all flags off. */
|
|
17
17
|
getFlags?: () => FeatureFlags;
|
|
18
18
|
}
|
|
19
|
-
export declare const buildOrchestratorReportPayload: (workerName: string, text: string, artifacts: string[], flags?: FeatureFlags) => string;
|
|
19
|
+
export declare const buildOrchestratorReportPayload: (workerName: string, text: string, artifacts: string[], flags?: FeatureFlags, dispatchId?: string) => string;
|
|
20
20
|
export declare const buildOrchestratorStatusPayload: (workerName: string, text: string, artifacts: string[], flags?: FeatureFlags) => string;
|
|
21
21
|
export declare const buildOrchestratorUserInputPayload: (text: string, flags?: FeatureFlags) => string;
|
|
22
22
|
export declare const buildWorkerDispatchPayload: (fromAgentName: string, workerDescription: string, dispatchId: string, text: string, memoryDigest?: string | null) => string;
|
|
23
23
|
export declare const buildWorkerCancelPayload: (dispatchId: string, reason: string) => string;
|
|
24
24
|
export declare const createAgentStdinDispatcher: ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, memoryInjection, getFlags, }: AgentStdinDispatcherInput) => {
|
|
25
|
-
writeReportPrompt(workspaceId: string, workerName: string, text: string, artifacts: string[], input?: {
|
|
26
|
-
requireActiveRun?: boolean;
|
|
27
|
-
}): void;
|
|
28
25
|
writeStatusPrompt(workspaceId: string, workerName: string, text: string, artifacts: string[], input?: {
|
|
29
26
|
requireActiveRun?: boolean;
|
|
30
27
|
}): void;
|
|
31
28
|
writeSendPrompt(workspaceId: string, workerId: string, dispatchId: string, fromAgentName: string, workerDescription: string, text: string): Promise<void>;
|
|
32
29
|
writeCancelPrompt(workspaceId: string, workerId: string, dispatchId: string, reason: string, input?: {
|
|
33
30
|
requireActiveRun?: boolean;
|
|
34
|
-
}): void
|
|
31
|
+
}): Promise<void>;
|
|
35
32
|
writeUserInputPrompt(workspaceId: string, text: string): void;
|
|
36
33
|
deliverUserInputToOrchestrator(workspaceId: string, text: string, input?: {
|
|
37
34
|
requireActiveRun?: boolean;
|
|
@@ -40,12 +37,6 @@ export declare const createAgentStdinDispatcher: ({ agentManager, getLaunchConfi
|
|
|
40
37
|
* Used by the workflow runner to notify the triggering orchestrator
|
|
41
38
|
* when a run finishes (mirrors Claude Code's <task-notification>). */
|
|
42
39
|
writeSystemMessageToAgent(workspaceId: string, agentId: string, text: string): void;
|
|
43
|
-
/** Awaitable report delivery — same payload as writeReportPrompt but the
|
|
44
|
-
* promise is returned so the caller can persist the report to the
|
|
45
|
-
* redelivery outbox if the orchestrator PTY write rejects. */
|
|
46
|
-
deliverReportToOrchestrator(workspaceId: string, workerName: string, text: string, artifacts: string[], input?: {
|
|
47
|
-
requireActiveRun?: boolean;
|
|
48
|
-
}): Promise<void>;
|
|
49
40
|
/** Awaitable opaque delivery — used to drain the report outbox so an entry
|
|
50
41
|
* is marked delivered only after the PTY write actually resolves. */
|
|
51
42
|
deliverSystemMessageToAgent(workspaceId: string, agentId: string, text: string, input?: {
|
|
@@ -4,9 +4,20 @@ import { buildOrchestratorReminderTail, buildWorkerReminderTail } from './hive-t
|
|
|
4
4
|
import { PtyInactiveError } from './http-errors.js';
|
|
5
5
|
import { createPostStartInputWriter } from './post-start-input-writer.js';
|
|
6
6
|
import { buildDispatchMemoryDigestSafely, logMemoryDigestInjection, rollbackMemoryDigestInjection, } from './team-memory-injection.js';
|
|
7
|
-
export const buildOrchestratorReportPayload = (workerName, text, artifacts, flags = FEATURE_FLAGS_ALL_OFF) => {
|
|
7
|
+
export const buildOrchestratorReportPayload = (workerName, text, artifacts, flags = FEATURE_FLAGS_ALL_OFF, dispatchId) => {
|
|
8
|
+
const dispatchAttribute = dispatchId
|
|
9
|
+
? ` dispatch="${escapeHiveEnvelopeAttribute(dispatchId)}"`
|
|
10
|
+
: '';
|
|
8
11
|
const lines = [
|
|
9
|
-
`<hive-message kind="report" from="@${escapeHiveEnvelopeAttribute(workerName)}">`,
|
|
12
|
+
`<hive-message kind="report" from="@${escapeHiveEnvelopeAttribute(workerName)}"${dispatchAttribute}>`,
|
|
13
|
+
...(dispatchId
|
|
14
|
+
? [
|
|
15
|
+
'',
|
|
16
|
+
`dispatch_id: ${escapeHiveEnvelopeText(dispatchId)}`,
|
|
17
|
+
'If this dispatch_id report was already processed, treat this as a duplicate redelivery and ignore it.',
|
|
18
|
+
'',
|
|
19
|
+
]
|
|
20
|
+
: []),
|
|
10
21
|
escapeHiveEnvelopeText(text),
|
|
11
22
|
];
|
|
12
23
|
for (const artifact of artifacts)
|
|
@@ -166,9 +177,6 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
|
|
|
166
177
|
});
|
|
167
178
|
};
|
|
168
179
|
return {
|
|
169
|
-
writeReportPrompt(workspaceId, workerName, text, artifacts, input = {}) {
|
|
170
|
-
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorReportPayload(workerName, text, artifacts, flags()), input));
|
|
171
|
-
},
|
|
172
180
|
writeStatusPrompt(workspaceId, workerName, text, artifacts, input = {}) {
|
|
173
181
|
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorStatusPayload(workerName, text, artifacts, flags()), input));
|
|
174
182
|
},
|
|
@@ -206,7 +214,7 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
|
|
|
206
214
|
}
|
|
207
215
|
},
|
|
208
216
|
writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input = {}) {
|
|
209
|
-
|
|
217
|
+
return writeToActiveAgentRun(workspaceId, workerId, buildWorkerCancelPayload(dispatchId, reason), input);
|
|
210
218
|
},
|
|
211
219
|
writeUserInputPrompt(workspaceId, text) {
|
|
212
220
|
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorUserInputPayload(text, flags())));
|
|
@@ -220,12 +228,6 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
|
|
|
220
228
|
writeSystemMessageToAgent(workspaceId, agentId, text) {
|
|
221
229
|
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, agentId, text));
|
|
222
230
|
},
|
|
223
|
-
/** Awaitable report delivery — same payload as writeReportPrompt but the
|
|
224
|
-
* promise is returned so the caller can persist the report to the
|
|
225
|
-
* redelivery outbox if the orchestrator PTY write rejects. */
|
|
226
|
-
deliverReportToOrchestrator(workspaceId, workerName, text, artifacts, input = {}) {
|
|
227
|
-
return writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorReportPayload(workerName, text, artifacts, flags()), input);
|
|
228
|
-
},
|
|
229
231
|
/** Awaitable opaque delivery — used to drain the report outbox so an entry
|
|
230
232
|
* is marked delivered only after the PTY write actually resolves. */
|
|
231
233
|
deliverSystemMessageToAgent(workspaceId, agentId, text, input = {}) {
|
|
@@ -8,7 +8,7 @@ const MIN_SUBMIT_AFTER_PASTE_DELAY_MS = 600;
|
|
|
8
8
|
const MAX_SUBMIT_AFTER_PASTE_DELAY_MS = 1500;
|
|
9
9
|
const PASTE_CHARS_PER_DELAY_MS = 4;
|
|
10
10
|
const PASTE_ACK_CHECK_INTERVAL_MS = 50;
|
|
11
|
-
const PASTE_ACK_SETTLE_DELAY_MS =
|
|
11
|
+
const PASTE_ACK_SETTLE_DELAY_MS = 250;
|
|
12
12
|
const CODEX_PASTE_ACK_SETTLE_DELAY_MS = 250;
|
|
13
13
|
const PASTE_ACK_TIMEOUT_MS = 3000;
|
|
14
14
|
const CLAUDE_HIVE_PASTE_ACK_TIMEOUT_MS = 10000;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import type { Database } from 'better-sqlite3';
|
|
2
2
|
/**
|
|
3
|
-
* Durable redelivery queue for
|
|
4
|
-
* receive. `team report
|
|
5
|
-
* report into the orchestrator's stdin
|
|
6
|
-
* PTY exited / is mid-restart, or the orchestrator was down at report
|
|
7
|
-
* the report would otherwise be lost — the ledger says "reported" yet the
|
|
8
|
-
* orchestrator waits forever, indistinguishable from a hang.
|
|
3
|
+
* Durable redelivery queue for protocol messages a live agent could not
|
|
4
|
+
* receive. The primary producer is `team report`: it persists the dispatch as
|
|
5
|
+
* reported and forwards the report into the orchestrator's stdin; if that write
|
|
6
|
+
* fails (the PTY exited / is mid-restart, or the orchestrator was down at report
|
|
7
|
+
* time) the report would otherwise be lost — the ledger says "reported" yet the
|
|
8
|
+
* orchestrator waits forever, indistinguishable from a hang. Dispatch-drop
|
|
9
|
+
* notifications use the same queue for their issuer.
|
|
9
10
|
*
|
|
10
11
|
* Entries are keyed by dispatch id (UNIQUE) so a report enqueues at most once,
|
|
11
|
-
* and drain marks them delivered only after the
|
|
12
|
-
*
|
|
12
|
+
* and drain marks them delivered only after the target PTY write actually
|
|
13
|
+
* resolves, so a failed redelivery stays pending for the next drain.
|
|
13
14
|
*/
|
|
14
15
|
export interface ReportOutboxEntry {
|
|
15
16
|
id: number;
|
|
@@ -27,6 +28,7 @@ interface EnqueueInput {
|
|
|
27
28
|
payload: string;
|
|
28
29
|
}
|
|
29
30
|
export declare const createReportOutboxStore: (db: Database) => {
|
|
31
|
+
deletePendingForDispatch: (dispatchId: string) => void;
|
|
30
32
|
enqueue: (input: EnqueueInput) => void;
|
|
31
33
|
listPending: (workspaceId: string, targetAgentId: string) => ReportOutboxEntry[];
|
|
32
34
|
markDelivered: (id: number) => void;
|
|
@@ -25,9 +25,18 @@ export const createReportOutboxStore = (db) => {
|
|
|
25
25
|
const markDelivered = (id) => {
|
|
26
26
|
db.prepare(`UPDATE report_outbox SET delivered_at = ? WHERE id = ? AND delivered_at IS NULL`).run(Date.now(), id);
|
|
27
27
|
};
|
|
28
|
+
const deletePendingForDispatch = (dispatchId) => {
|
|
29
|
+
db.prepare('DELETE FROM report_outbox WHERE dispatch_id = ? AND delivered_at IS NULL').run(dispatchId);
|
|
30
|
+
};
|
|
28
31
|
const pendingCount = (workspaceId, targetAgentId) => db
|
|
29
32
|
.prepare(`SELECT COUNT(*) AS n FROM report_outbox
|
|
30
33
|
WHERE workspace_id = ? AND target_agent_id = ? AND delivered_at IS NULL`)
|
|
31
34
|
.get(workspaceId, targetAgentId).n;
|
|
32
|
-
return {
|
|
35
|
+
return {
|
|
36
|
+
deletePendingForDispatch,
|
|
37
|
+
enqueue,
|
|
38
|
+
listPending,
|
|
39
|
+
markDelivered,
|
|
40
|
+
pendingCount,
|
|
41
|
+
};
|
|
33
42
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ForbiddenError } from './http-errors.js';
|
|
2
2
|
import { REMOTE_DAEMON_TOKEN_KEY, REMOTE_ENABLED_KEY, REMOTE_GATEWAY_URL_KEY, } from './remote-config-keys.js';
|
|
3
|
+
import { HIVE_REMOTE_DEVICE_HEADER } from './remote-loopback-auth.js';
|
|
3
4
|
import { getRequiredParam, readJsonBody, route, sendJson } from './route-helpers.js';
|
|
4
5
|
import { requireUiTokenFromRequest } from './ui-auth-helpers.js';
|
|
5
6
|
// Remote-access device-management + pairing routes (M4). Two gate classes:
|
|
@@ -23,8 +24,11 @@ const gateUi = ({ request, store }) => {
|
|
|
23
24
|
// check, so even the short-circuit can't admit a tunnel request that slipped a cookie too.
|
|
24
25
|
const gateLocalDesktopOnly = ({ request, store }) => {
|
|
25
26
|
if (store.authorizeRemoteTunnelRequest(request)) {
|
|
27
|
+
const rawDeviceId = request.headers[HIVE_REMOTE_DEVICE_HEADER];
|
|
26
28
|
store.getRemoteAuditStore().enqueue({
|
|
27
29
|
action: 'reject',
|
|
30
|
+
deviceId: typeof rawDeviceId === 'string' ? rawDeviceId : null,
|
|
31
|
+
endpoint: request.url ?? null,
|
|
28
32
|
result: 'rejected',
|
|
29
33
|
rejectReason: 'pairing_confirm_forbidden',
|
|
30
34
|
});
|
|
@@ -38,8 +42,8 @@ const remoteStatus = (store) => {
|
|
|
38
42
|
const status = store.getRemoteTunnelStatus();
|
|
39
43
|
return {
|
|
40
44
|
enabled: store.settings.getAppState(REMOTE_ENABLED_KEY)?.value === 'true',
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
logged_in: isLoggedIn(store),
|
|
46
|
+
gateway_url: store.settings.getAppState(REMOTE_GATEWAY_URL_KEY)?.value ?? null,
|
|
43
47
|
connected: status === 'online',
|
|
44
48
|
// The FULL tunnel state, not just online/offline — the desktop dot was stuck looking "connected or
|
|
45
49
|
// not" and couldn't show connecting / reconnecting / revoked / logged-out. (connecting+reconnecting
|
|
@@ -47,15 +51,41 @@ const remoteStatus = (store) => {
|
|
|
47
51
|
connection: status,
|
|
48
52
|
};
|
|
49
53
|
};
|
|
50
|
-
//
|
|
51
|
-
|
|
54
|
+
// HTTP responses stay snake_case even though the store uses camelCase records.
|
|
55
|
+
const toPairingTicketView = (ticket) => ({
|
|
56
|
+
pairing_id: ticket.pairingId,
|
|
57
|
+
qr: ticket.qr,
|
|
58
|
+
code: ticket.code,
|
|
59
|
+
expires_at: ticket.expiresAt,
|
|
60
|
+
});
|
|
61
|
+
const toPendingPairingView = (pending) => ({
|
|
62
|
+
pairing_id: pending.pairingId,
|
|
63
|
+
device_name: pending.deviceName,
|
|
64
|
+
sas: pending.sas,
|
|
65
|
+
expires_at: pending.expiresAt,
|
|
66
|
+
});
|
|
52
67
|
const toDeviceView = (record) => ({
|
|
53
68
|
id: record.id,
|
|
54
69
|
name: record.name,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
70
|
+
created_at: record.createdAt,
|
|
71
|
+
last_active: record.lastActive,
|
|
72
|
+
revoked_at: record.revokedAt,
|
|
73
|
+
});
|
|
74
|
+
const toAuditView = (record) => ({
|
|
75
|
+
id: record.id,
|
|
76
|
+
device_id: record.deviceId,
|
|
77
|
+
ts: record.ts,
|
|
78
|
+
workspace_id: record.workspaceId,
|
|
79
|
+
action: record.action,
|
|
80
|
+
endpoint: record.endpoint,
|
|
81
|
+
result: record.result,
|
|
82
|
+
reject_reason: record.rejectReason,
|
|
83
|
+
byte_count: record.byteCount,
|
|
84
|
+
preview: record.preview,
|
|
58
85
|
});
|
|
86
|
+
const getSearchParams = (url) => new URL(url ?? '', 'http://x').searchParams;
|
|
87
|
+
const hasCompatFlag = (params, snakeName, camelName) => params.has(snakeName) || params.has(camelName);
|
|
88
|
+
const getCompatParam = (params, snakeName, camelName) => params.get(snakeName) ?? params.get(camelName);
|
|
59
89
|
export const remoteRoutes = [
|
|
60
90
|
route('GET', '/api/remote/status', (ctx) => {
|
|
61
91
|
gateUi(ctx);
|
|
@@ -83,7 +113,7 @@ export const remoteRoutes = [
|
|
|
83
113
|
}
|
|
84
114
|
try {
|
|
85
115
|
const ticket = ctx.store.getRemotePairing().beginPairing();
|
|
86
|
-
sendJson(ctx.response, 200, ticket);
|
|
116
|
+
sendJson(ctx.response, 200, toPairingTicketView(ticket));
|
|
87
117
|
}
|
|
88
118
|
catch {
|
|
89
119
|
// beginPairing throws if gateway/daemon id vanished between the status read and here.
|
|
@@ -94,7 +124,7 @@ export const remoteRoutes = [
|
|
|
94
124
|
// HARDEN minor: desktop-only. The confirm dialog backs this; a phone has no use for another
|
|
95
125
|
// device's in-flight SAS, and exposing it would needlessly widen the secret surface (invariant 7).
|
|
96
126
|
gateLocalDesktopOnly(ctx);
|
|
97
|
-
sendJson(ctx.response, 200, ctx.store.getRemotePairing().listPending());
|
|
127
|
+
sendJson(ctx.response, 200, ctx.store.getRemotePairing().listPending().map(toPendingPairingView));
|
|
98
128
|
}),
|
|
99
129
|
route('POST', '/api/remote/pairings/:pairingId/confirm', async (ctx) => {
|
|
100
130
|
const { params, request, response, store } = ctx;
|
|
@@ -139,7 +169,8 @@ export const remoteRoutes = [
|
|
|
139
169
|
// ── equal-authority device management ──────────────────────────────────────────────────────────
|
|
140
170
|
route('GET', '/api/remote/devices', (ctx) => {
|
|
141
171
|
gateUi(ctx);
|
|
142
|
-
const
|
|
172
|
+
const params = getSearchParams(ctx.request.url);
|
|
173
|
+
const includeRevoked = hasCompatFlag(params, 'include_revoked', 'includeRevoked');
|
|
143
174
|
sendJson(ctx.response, 200, ctx.store.getRemoteDeviceStore().list(includeRevoked).map(toDeviceView));
|
|
144
175
|
}),
|
|
145
176
|
route('POST', '/api/remote/devices/:deviceId/revoke', (ctx) => {
|
|
@@ -156,11 +187,12 @@ export const remoteRoutes = [
|
|
|
156
187
|
}),
|
|
157
188
|
route('GET', '/api/remote/audit', (ctx) => {
|
|
158
189
|
gateUi(ctx);
|
|
159
|
-
const
|
|
160
|
-
const rawLimit = Number(
|
|
190
|
+
const params = getSearchParams(ctx.request.url);
|
|
191
|
+
const rawLimit = Number(params.get('limit'));
|
|
161
192
|
const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 500) : 100;
|
|
162
|
-
const deviceId =
|
|
193
|
+
const deviceId = getCompatParam(params, 'device_id', 'deviceId');
|
|
163
194
|
const audit = ctx.store.getRemoteAuditStore();
|
|
164
|
-
|
|
195
|
+
const records = deviceId ? audit.listForDevice(deviceId, limit) : audit.list(limit);
|
|
196
|
+
sendJson(ctx.response, 200, records.map(toAuditView));
|
|
165
197
|
}),
|
|
166
198
|
];
|
|
@@ -306,7 +306,7 @@ export const teamRoutes = [
|
|
|
306
306
|
workspaceId: projectId,
|
|
307
307
|
});
|
|
308
308
|
requireCommandForRole(agent, 'cancel');
|
|
309
|
-
const result = store.cancelTask(projectId, dispatchId, { fromAgentId, reason });
|
|
309
|
+
const result = await store.cancelTask(projectId, dispatchId, { fromAgentId, reason });
|
|
310
310
|
sendJson(response, 202, {
|
|
311
311
|
dispatch_id: result.dispatch?.id ?? null,
|
|
312
312
|
forward_error: result.forwardError,
|
|
@@ -109,11 +109,9 @@ export const workspaceRoutes = [
|
|
|
109
109
|
workspaceId,
|
|
110
110
|
});
|
|
111
111
|
requireCommandForRole(agent, 'list');
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
// for non-orchestrator callers.
|
|
116
|
-
store.drainReportOutbox(workspaceId);
|
|
112
|
+
// Polling `team list` is a natural post-restart wakeup: flush any durable
|
|
113
|
+
// notices stranded for the caller now that its PTY is reachable again.
|
|
114
|
+
store.drainReportOutbox(workspaceId, agent.id);
|
|
117
115
|
sendJson(response, 200, serializeTeamListWithOpenDispatches(store, workspaceId));
|
|
118
116
|
}),
|
|
119
117
|
route('POST', '/api/workspaces/:workspaceId/workers', async ({ params, request, response, store }) => {
|
|
@@ -198,7 +196,6 @@ export const workspaceRoutes = [
|
|
|
198
196
|
}
|
|
199
197
|
const orchestratorId = getOrchestratorId(workspaceId);
|
|
200
198
|
if (!store.getActiveRunByAgentId(workspaceId, orchestratorId)) {
|
|
201
|
-
store.recordUserInput(workspaceId, orchestratorId, body.text);
|
|
202
199
|
throw new PtyInactiveError(`No active run for agent: ${orchestratorId}`);
|
|
203
200
|
}
|
|
204
201
|
await store.deliverUserInput(workspaceId, orchestratorId, body.text);
|
|
@@ -44,9 +44,9 @@ export interface RuntimeStore {
|
|
|
44
44
|
reportTask: (workspaceId: string, workerId: string, input?: ReportTaskInput) => ReportTaskResult;
|
|
45
45
|
/** Flush any reports stranded by a prior orchestrator outage. Safe no-op
|
|
46
46
|
* when the orchestrator is down or the outbox is empty. */
|
|
47
|
-
drainReportOutbox: (workspaceId: string) => void;
|
|
47
|
+
drainReportOutbox: (workspaceId: string, targetAgentId?: string) => void;
|
|
48
48
|
statusTask: (workspaceId: string, workerId: string, input?: StatusTaskInput) => ReportTaskResult;
|
|
49
|
-
cancelTask: (workspaceId: string, dispatchId: string, input: CancelTaskInput) => ReportTaskResult
|
|
49
|
+
cancelTask: (workspaceId: string, dispatchId: string, input: CancelTaskInput) => Promise<ReportTaskResult>;
|
|
50
50
|
listDispatches: (workspaceId: string, options?: ListDispatchesOptions) => DispatchRecord[];
|
|
51
51
|
listOpenDispatches: (workspaceId: string) => DispatchRecord[];
|
|
52
52
|
listRecentDispatches: (workspaceId: string, limit?: number) => DispatchRecord[];
|
|
@@ -10,6 +10,7 @@ import { type RemoteAuditStore } from './remote-audit-store.js';
|
|
|
10
10
|
import type { DeviceSessionProvider } from './remote-device-session.js';
|
|
11
11
|
import { type RemoteDeviceStore } from './remote-device-store.js';
|
|
12
12
|
import { type RemotePairing } from './remote-pairing.js';
|
|
13
|
+
import { createReportOutboxStore } from './report-outbox-store.js';
|
|
13
14
|
import { openRuntimeDatabase } from './runtime-database.js';
|
|
14
15
|
import { createSentinelHeartbeatService } from './sentinel-heartbeat.js';
|
|
15
16
|
import { createSettingsStore } from './settings-store.js';
|
|
@@ -39,6 +40,8 @@ export interface RuntimeStoreServices {
|
|
|
39
40
|
agentRuntime: ReturnType<typeof createAgentRuntime>;
|
|
40
41
|
db: ReturnType<typeof openRuntimeDatabase>;
|
|
41
42
|
dispatchLedgerStore: ReturnType<typeof createDispatchLedgerStore>;
|
|
43
|
+
isRuntimeClosing: () => boolean;
|
|
44
|
+
markRuntimeClosing: () => void;
|
|
42
45
|
messageLogStore: ReturnType<typeof createMessageLogStore>;
|
|
43
46
|
remoteAuditStore: RemoteAuditStore;
|
|
44
47
|
remoteDeviceSessions: DeviceSessionProvider;
|
|
@@ -53,6 +56,7 @@ export interface RuntimeStoreServices {
|
|
|
53
56
|
teamMemoryDreamScheduler: ReturnType<typeof createTeamMemoryDreamScheduler>;
|
|
54
57
|
sentinelHeartbeat: ReturnType<typeof createSentinelHeartbeatService>;
|
|
55
58
|
protocolEventStats: ReturnType<typeof createProtocolEventStats>;
|
|
59
|
+
reportOutbox: ReturnType<typeof createReportOutboxStore>;
|
|
56
60
|
teamMemoryDreamStore: ReturnType<typeof createTeamMemoryDreamStore>;
|
|
57
61
|
teamMemoryDiagnostics: ReturnType<typeof createTeamMemoryDiagnostics>;
|
|
58
62
|
teamMemoryStore: ReturnType<typeof createTeamMemoryStore>;
|
|
@@ -76,6 +76,7 @@ const createWorkspaceUploadStorage = (dataDir) => {
|
|
|
76
76
|
};
|
|
77
77
|
export const createRuntimeStoreServices = (options = {}) => {
|
|
78
78
|
const db = openRuntimeDatabase(options.dataDir);
|
|
79
|
+
let closing = false;
|
|
79
80
|
const uploadStorage = createWorkspaceUploadStorage(options.dataDir);
|
|
80
81
|
const messageLogStore = createMessageLogStore(db);
|
|
81
82
|
const dispatchLedgerStore = createDispatchLedgerStore(db);
|
|
@@ -239,18 +240,24 @@ export const createRuntimeStoreServices = (options = {}) => {
|
|
|
239
240
|
// agentRuntime is assigned.
|
|
240
241
|
function removeWorkerCompletely(workspaceId, workerId) {
|
|
241
242
|
const activeRun = agentRuntime.getActiveRunByAgentId(workspaceId, workerId);
|
|
242
|
-
|
|
243
|
-
// first, or parked/in-flight work vanishes silently (review finding).
|
|
244
|
-
try {
|
|
245
|
-
teamOps.notifyIssuersOfDroppedDispatches(workspaceId, workerId, 'the worker was dismissed');
|
|
246
|
-
}
|
|
247
|
-
catch (error) {
|
|
248
|
-
console.error('[hive] swallowed:removeWorker.notifyDropped', error);
|
|
249
|
-
}
|
|
243
|
+
const droppedNoticeTargets = new Set();
|
|
250
244
|
db.transaction(() => {
|
|
245
|
+
for (const dispatch of dispatchLedgerStore.listWorkspaceDispatches(workspaceId)) {
|
|
246
|
+
if (dispatch.toAgentId === workerId && dispatch.workflowRunId === null) {
|
|
247
|
+
reportOutbox.deletePendingForDispatch(dispatch.id);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Open dispatch rows are about to be hard-deleted — queue issuer notices
|
|
251
|
+
// in the same transaction or the delete must not commit.
|
|
252
|
+
for (const targetAgentId of teamOps.notifyIssuersOfDroppedDispatches(workspaceId, workerId, 'the worker was dismissed')) {
|
|
253
|
+
droppedNoticeTargets.add(targetAgentId);
|
|
254
|
+
}
|
|
251
255
|
dispatchLedgerStore.deleteWorkerDispatches(workspaceId, workerId);
|
|
252
256
|
workspaceStore.deleteWorker(workspaceId, workerId);
|
|
253
257
|
})();
|
|
258
|
+
for (const targetAgentId of droppedNoticeTargets) {
|
|
259
|
+
teamOps.drainReportOutbox(workspaceId, targetAgentId);
|
|
260
|
+
}
|
|
254
261
|
agentRuntime.deleteAgentLaunchConfig(workspaceId, workerId);
|
|
255
262
|
if (activeRun)
|
|
256
263
|
agentRuntime.stopAgentRun(activeRun.runId);
|
|
@@ -283,6 +290,8 @@ export const createRuntimeStoreServices = (options = {}) => {
|
|
|
283
290
|
reparkClaimedDispatch: dispatchLedgerStore.reparkClaimedDispatch,
|
|
284
291
|
reportOutbox,
|
|
285
292
|
notifyWebhook: webhookNotifier.notify,
|
|
293
|
+
runDataMutation: (mutation) => db.transaction(mutation)(),
|
|
294
|
+
isRuntimeClosing: () => closing,
|
|
286
295
|
workflowDispatchAwaiter,
|
|
287
296
|
workspaceStore,
|
|
288
297
|
dismissEphemeralWorker: (workspaceId, workerId) => removeWorkerCompletely(workspaceId, workerId),
|
|
@@ -296,6 +305,10 @@ export const createRuntimeStoreServices = (options = {}) => {
|
|
|
296
305
|
agentRuntime,
|
|
297
306
|
db,
|
|
298
307
|
dispatchLedgerStore,
|
|
308
|
+
isRuntimeClosing: () => closing,
|
|
309
|
+
markRuntimeClosing: () => {
|
|
310
|
+
closing = true;
|
|
311
|
+
},
|
|
299
312
|
messageLogStore,
|
|
300
313
|
remoteAuditStore,
|
|
301
314
|
remoteDeviceSessions,
|
|
@@ -310,6 +323,7 @@ export const createRuntimeStoreServices = (options = {}) => {
|
|
|
310
323
|
teamMemoryDreamScheduler,
|
|
311
324
|
sentinelHeartbeat,
|
|
312
325
|
protocolEventStats,
|
|
326
|
+
reportOutbox,
|
|
313
327
|
teamMemoryDreamStore,
|
|
314
328
|
teamMemoryDiagnostics,
|
|
315
329
|
teamMemoryExport,
|
|
@@ -345,7 +359,15 @@ export const createRuntimeStoreLifecycle = ({ agentManager, services, }) => {
|
|
|
345
359
|
// for orchestrator/workflow pseudo-agents, which never hold queued
|
|
346
360
|
// worker dispatches addressed to themselves). Wait for post-start
|
|
347
361
|
// guidance first so startup instructions never race the first task.
|
|
348
|
-
const
|
|
362
|
+
const deliverPostStartBacklog = () => {
|
|
363
|
+
if (services.isRuntimeClosing())
|
|
364
|
+
return;
|
|
365
|
+
try {
|
|
366
|
+
services.teamOps.drainReportOutbox(workspaceId, agentId);
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
console.error('[hive] swallowed:startAgent.drainOutbox', error);
|
|
370
|
+
}
|
|
349
371
|
try {
|
|
350
372
|
services.teamOps.replayQueuedDispatches(workspaceId, agentId, {
|
|
351
373
|
createdBeforeMs: run.startedAt,
|
|
@@ -356,12 +378,12 @@ export const createRuntimeStoreLifecycle = ({ agentManager, services, }) => {
|
|
|
356
378
|
}
|
|
357
379
|
};
|
|
358
380
|
if (run.postStartInputReady) {
|
|
359
|
-
void run.postStartInputReady.then(
|
|
381
|
+
void run.postStartInputReady.then(deliverPostStartBacklog).catch((error) => {
|
|
360
382
|
console.error('[hive] swallowed:startAgent.replayAfterPostStart', error);
|
|
361
383
|
});
|
|
362
384
|
}
|
|
363
385
|
else {
|
|
364
|
-
|
|
386
|
+
deliverPostStartBacklog();
|
|
365
387
|
}
|
|
366
388
|
}
|
|
367
389
|
return run;
|
|
@@ -406,6 +428,7 @@ export const createRuntimeStoreLifecycle = ({ agentManager, services, }) => {
|
|
|
406
428
|
};
|
|
407
429
|
return {
|
|
408
430
|
close: async () => {
|
|
431
|
+
services.markRuntimeClosing();
|
|
409
432
|
try {
|
|
410
433
|
// Fail in-flight workflow awaiters BEFORE their workers vanish, so the
|
|
411
434
|
// runner's `await agent(...)` rejects with a clear shutdown error
|
|
@@ -29,16 +29,22 @@ export const createRuntimeStoreWorkerMutations = ({ addWorker, configureAgentLau
|
|
|
29
29
|
},
|
|
30
30
|
deleteWorker: (workspaceId, workerId) => {
|
|
31
31
|
const activeRun = services.agentRuntime.getActiveRunByAgentId(workspaceId, workerId);
|
|
32
|
-
|
|
33
|
-
services.teamOps.notifyIssuersOfDroppedDispatches(workspaceId, workerId, 'the worker was removed');
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
console.error('[hive] swallowed:deleteWorker.notifyDropped', error);
|
|
37
|
-
}
|
|
32
|
+
const droppedNoticeTargets = new Set();
|
|
38
33
|
runDataMutation(() => {
|
|
34
|
+
for (const dispatch of services.dispatchLedgerStore.listWorkspaceDispatches(workspaceId)) {
|
|
35
|
+
if (dispatch.toAgentId === workerId && dispatch.workflowRunId === null) {
|
|
36
|
+
services.reportOutbox.deletePendingForDispatch(dispatch.id);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const targetAgentId of services.teamOps.notifyIssuersOfDroppedDispatches(workspaceId, workerId, 'the worker was removed')) {
|
|
40
|
+
droppedNoticeTargets.add(targetAgentId);
|
|
41
|
+
}
|
|
39
42
|
services.dispatchLedgerStore.deleteWorkerDispatches(workspaceId, workerId);
|
|
40
43
|
services.workspaceStore.deleteWorker(workspaceId, workerId);
|
|
41
44
|
});
|
|
45
|
+
for (const targetAgentId of droppedNoticeTargets) {
|
|
46
|
+
services.teamOps.drainReportOutbox(workspaceId, targetAgentId);
|
|
47
|
+
}
|
|
42
48
|
services.agentRuntime.deleteAgentLaunchConfig(workspaceId, workerId);
|
|
43
49
|
if (activeRun)
|
|
44
50
|
services.agentRuntime.stopAgentRun(activeRun.runId);
|
|
@@ -23,7 +23,13 @@ export const createRuntimeStoreWorkflowRuntime = (services, store) => {
|
|
|
23
23
|
return false;
|
|
24
24
|
services.workspaceStore.markTaskCancelled(workspaceId, openDispatch.toAgentId);
|
|
25
25
|
try {
|
|
26
|
-
services.agentRuntime
|
|
26
|
+
void services.agentRuntime
|
|
27
|
+
.writeCancelPrompt(workspaceId, cancelled.toAgentId, cancelled.id, reason, {
|
|
28
|
+
requireActiveRun: true,
|
|
29
|
+
})
|
|
30
|
+
.catch((error) => {
|
|
31
|
+
console.error('[hive] swallowed:workflow.cancelDispatch.forward', error);
|
|
32
|
+
});
|
|
27
33
|
}
|
|
28
34
|
catch (error) {
|
|
29
35
|
console.error('[hive] swallowed:workflow.cancelDispatch.forward', error);
|
|
@@ -46,6 +46,10 @@ export interface TeamOperationsInput {
|
|
|
46
46
|
reportOutbox: ReportOutboxStore;
|
|
47
47
|
/** Fire an outbound completion webhook (best-effort) when a worker reports. */
|
|
48
48
|
notifyWebhook?: (event: WebhookEvent) => void;
|
|
49
|
+
/** Runs durable protocol writes atomically when backed by SQLite. */
|
|
50
|
+
runDataMutation?: (mutation: () => void) => void;
|
|
51
|
+
/** True once runtime shutdown has begun; late async PTY callbacks must not touch SQLite. */
|
|
52
|
+
isRuntimeClosing?: () => boolean;
|
|
49
53
|
workflowDispatchAwaiter: WorkflowDispatchAwaiter;
|
|
50
54
|
workspaceStore: WorkspaceStore;
|
|
51
55
|
/** Auto-dismiss an ephemeral orchestrator-spawned worker after its
|
|
@@ -90,20 +94,23 @@ export interface ReportTaskResult {
|
|
|
90
94
|
forwarded: boolean;
|
|
91
95
|
pendingWarning?: string;
|
|
92
96
|
}
|
|
93
|
-
export declare const createTeamOperations: ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, listOpenWorkspaceDispatches, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, claimQueuedDispatch, reparkClaimedDispatch, reportOutbox, notifyWebhook, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, recordProtocolEvent, getFlags, }: TeamOperationsInput) => {
|
|
94
|
-
cancelTask(workspaceId: string, dispatchId: string, input: CancelTaskInput): {
|
|
97
|
+
export declare const createTeamOperations: ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, listOpenWorkspaceDispatches, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, claimQueuedDispatch, reparkClaimedDispatch, reportOutbox, notifyWebhook, runDataMutation, isRuntimeClosing, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, recordProtocolEvent, getFlags, }: TeamOperationsInput) => {
|
|
98
|
+
cancelTask(workspaceId: string, dispatchId: string, input: CancelTaskInput): Promise<{
|
|
95
99
|
dispatch: DispatchRecord;
|
|
96
100
|
forwardError: string | null;
|
|
97
101
|
forwarded: boolean;
|
|
98
|
-
}
|
|
102
|
+
}>;
|
|
99
103
|
dispatchTask: (workspaceId: string, workerId: string, text: string, input?: DispatchTaskInput) => Promise<DispatchRecord>;
|
|
100
|
-
drainReportOutbox: (workspaceId: string) =>
|
|
104
|
+
drainReportOutbox: (workspaceId: string, targetAgentId?: string) => {
|
|
105
|
+
attempted: number;
|
|
106
|
+
firstSyncError: string | null;
|
|
107
|
+
};
|
|
101
108
|
replayQueuedDispatches: (workspaceId: string, workerId: string, options?: {
|
|
102
109
|
beforeSequence?: number;
|
|
103
110
|
createdBeforeMs?: number;
|
|
104
111
|
excludeDispatchId?: string;
|
|
105
112
|
}) => void;
|
|
106
|
-
notifyIssuersOfDroppedDispatches: (workspaceId: string, workerId: string, reason: string) =>
|
|
113
|
+
notifyIssuersOfDroppedDispatches: (workspaceId: string, workerId: string, reason: string) => string[];
|
|
107
114
|
dispatchTaskByWorkerName(workspaceId: string, workerName: string, text: string, input?: DispatchTaskInput): Promise<DispatchRecord & {
|
|
108
115
|
restartedWorker: boolean;
|
|
109
116
|
}>;
|