@tt-a1i/hive 2.1.14 → 2.1.15
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 +17 -0
- package/dist/src/cli/team.js +19 -6
- package/dist/src/server/agent-startup-instructions.d.ts +1 -0
- package/dist/src/server/agent-startup-instructions.js +4 -1
- package/dist/src/server/hive-team-guidance.js +2 -0
- package/dist/src/server/route-types.d.ts +1 -0
- package/dist/src/server/routes-team.js +3 -0
- package/dist/src/server/routes-workers.d.ts +2 -0
- package/dist/src/server/routes-workers.js +112 -0
- package/dist/src/server/routes-workspaces.js +4 -85
- package/dist/src/server/routes.js +2 -0
- package/dist/src/server/runtime-store-contract.d.ts +5 -0
- package/dist/src/server/runtime-store.js +2 -0
- package/dist/src/server/sqlite-schema-v39.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v39.js +6 -0
- package/dist/src/server/sqlite-schema.d.ts +1 -1
- package/dist/src/server/sqlite-schema.js +8 -1
- package/dist/src/server/team-list-serializer.d.ts +5 -1
- package/dist/src/server/team-list-serializer.js +2 -1
- package/dist/src/server/team-memory-dream-prompt.js +18 -5
- package/dist/src/server/team-operations.d.ts +4 -0
- package/dist/src/server/team-operations.js +18 -5
- package/dist/src/server/workspace-store-contract.d.ts +6 -0
- package/dist/src/server/workspace-store-hydration.js +2 -1
- package/dist/src/server/workspace-store-support.d.ts +1 -0
- package/dist/src/server/workspace-store.js +57 -2
- package/dist/src/shared/types.d.ts +4 -0
- package/dist/src/shared/worker-avatar.d.ts +2 -0
- package/dist/src/shared/worker-avatar.js +55 -0
- package/package.json +1 -1
- package/web/dist/assets/AddWorkerDialog-Dfg4e74k.js +2 -0
- package/web/dist/assets/AddWorkspaceFlow-Bt5i_6p3.js +1 -0
- package/web/dist/assets/FirstRunWizard--NAbC5zC.js +1 -0
- package/web/dist/assets/{MarketplaceDrawer-BHTvxLMz.js → MarketplaceDrawer-CJhft0Xd.js} +1 -1
- package/web/dist/assets/{TaskGraphDrawer-woCXhUwp.js → TaskGraphDrawer-BUCR4zPH.js} +1 -1
- package/web/dist/assets/WhatsNewDialog-B05r5jd4.js +1 -0
- package/web/dist/assets/WorkerModal-CSM87uaz.js +1 -0
- package/web/dist/assets/{WorkflowsDrawer-Diw0TiiJ.js → WorkflowsDrawer-pBezljLE.js} +1 -1
- package/web/dist/assets/WorkspaceMemoryDrawer-Akq36Iaz.js +1 -0
- package/web/dist/assets/{WorkspaceTaskDrawer-D10RcAUj.js → WorkspaceTaskDrawer-CDhs_vbw.js} +1 -1
- package/web/dist/assets/index-BTPjdp2B.js +84 -0
- package/web/dist/assets/index-Cs7E1UKt.css +1 -0
- package/web/dist/assets/{search-D6bjVJKH.js → search-CWX42x_k.js} +1 -1
- package/web/dist/assets/{square-terminal-BUBEjTQZ.js → square-terminal-DoiN9oxG.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/dist/sw.js +1 -1
- package/web/dist/assets/AddWorkerDialog-BdIc_ZDz.js +0 -2
- package/web/dist/assets/AddWorkspaceFlow-h-UH433o.js +0 -1
- package/web/dist/assets/FirstRunWizard-ChuB1siy.js +0 -1
- package/web/dist/assets/WhatsNewDialog-BcRbRAJI.js +0 -1
- package/web/dist/assets/WorkerModal-CTf1k881.js +0 -1
- package/web/dist/assets/WorkspaceMemoryDrawer-B7Z1_COx.js +0 -1
- package/web/dist/assets/index-BTaZgzo0.js +0 -84
- package/web/dist/assets/index-DYIPzuPD.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-facing changes will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 2.1.15 - 2026-07-02
|
|
6
|
+
|
|
7
|
+
Worker avatars, terminal rendering reliability, and clearer team report delivery.
|
|
8
|
+
|
|
9
|
+
- Adds custom worker avatars, including upload, crop, validation, persistence,
|
|
10
|
+
and display across the team UI.
|
|
11
|
+
- Keeps worker avatars out of CLI team-list responses while still showing them
|
|
12
|
+
in the browser workspace.
|
|
13
|
+
- Improves terminal rendering under heavy output and reconnect/restore flows,
|
|
14
|
+
reducing stale or duplicated terminal frames.
|
|
15
|
+
- Tightens Dream memory consolidation instructions so procedure references are
|
|
16
|
+
only used for real saved workflows, skills, procedures, templates, or docs.
|
|
17
|
+
- Makes `team report` delivery wording more accurate when Hive accepts a report
|
|
18
|
+
and durable Orchestrator delivery is still in progress.
|
|
19
|
+
- Adjusts Orchestrator guidance so accepted dispatches are usually left alone
|
|
20
|
+
until a member reports, reducing premature status checks or cancels.
|
|
21
|
+
|
|
5
22
|
## 2.1.14 - 2026-06-25
|
|
6
23
|
|
|
7
24
|
Terminal focus and Dream memory reliability.
|
package/dist/src/cli/team.js
CHANGED
|
@@ -124,6 +124,23 @@ const GOAL_REPORT_USAGE = 'Usage: team goal report --goal <goal-id> --status pro
|
|
|
124
124
|
const GOAL_REPORT_STATUSES = new Set(['progress', 'done', 'blocked', 'failed']);
|
|
125
125
|
const usageFor = (command) => (command === 'status' ? STATUS_USAGE : REPORT_USAGE);
|
|
126
126
|
const withUsage = (message, command) => `${message}\n\n${usageFor(command)}`;
|
|
127
|
+
const printOrchestratorDeliveryWarning = (action, payload) => {
|
|
128
|
+
if (payload.delivery_state === 'delivered' || payload.delivery_state === 'delivering')
|
|
129
|
+
return;
|
|
130
|
+
if (payload.delivery_state === 'queued') {
|
|
131
|
+
const detail = payload.forward_error ? `: ${payload.forward_error}` : '.';
|
|
132
|
+
console.error(`Hive recorded the ${action}, but Orchestrator did not receive it in real time. It is queued for durable delivery${detail}`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (payload.delivery_state === 'failed') {
|
|
136
|
+
const detail = payload.forward_error ? `: ${payload.forward_error}` : '.';
|
|
137
|
+
console.error(`Hive recorded the ${action}, but could not deliver it to Orchestrator or confirm durable queueing${detail}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (payload.forwarded === false && payload.forward_error) {
|
|
141
|
+
console.error(`Hive recorded the ${action}, but could not deliver it to Orchestrator in real time: ${payload.forward_error}`);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
127
144
|
const readGeneratedProtocolGuide = (topic) => {
|
|
128
145
|
const protocolPath = join(process.cwd(), '.hive', 'PROTOCOL.md');
|
|
129
146
|
if (!existsSync(protocolPath))
|
|
@@ -1043,9 +1060,7 @@ export const runTeamCommand = async (argv) => {
|
|
|
1043
1060
|
artifacts: report.artifacts,
|
|
1044
1061
|
});
|
|
1045
1062
|
const payload = (await response.json());
|
|
1046
|
-
|
|
1047
|
-
console.error(`Hive recorded the status update, but could not deliver it to Orchestrator in real time: ${payload.forward_error}`);
|
|
1048
|
-
}
|
|
1063
|
+
printOrchestratorDeliveryWarning('status update', payload);
|
|
1049
1064
|
if (payload.pending_warning)
|
|
1050
1065
|
console.error(payload.pending_warning);
|
|
1051
1066
|
return;
|
|
@@ -1064,9 +1079,7 @@ export const runTeamCommand = async (argv) => {
|
|
|
1064
1079
|
artifacts: report.artifacts,
|
|
1065
1080
|
});
|
|
1066
1081
|
const payload = (await response.json());
|
|
1067
|
-
|
|
1068
|
-
console.error(`Hive recorded the report, but could not deliver it to Orchestrator in real time: ${payload.forward_error}`);
|
|
1069
|
-
}
|
|
1082
|
+
printOrchestratorDeliveryWarning('report', payload);
|
|
1070
1083
|
if (payload.pending_warning)
|
|
1071
1084
|
console.error(payload.pending_warning);
|
|
1072
1085
|
return;
|
|
@@ -9,6 +9,7 @@ export declare const buildAgentLegacyIdentityMarker: ({ agent, workspace, }: {
|
|
|
9
9
|
workspace: WorkspaceSummary;
|
|
10
10
|
}) => string;
|
|
11
11
|
export declare const WORKER_STARTUP_READY_STATUS = "ready: startup prompt received; Hive CLI channel OK; waiting for dispatch.";
|
|
12
|
+
export declare const REVIEWER_STARTUP_READY_STATUS = "ready: reviewer available as the final review gate. If review finds issues, have them fixed and send the work back to me until no issues remain before reporting completion to the user.";
|
|
12
13
|
export declare const buildAgentStartupInstructions: ({ agent, memoryDigest, workspace, flags, }: {
|
|
13
14
|
agent: AgentSummary;
|
|
14
15
|
memoryDigest?: string | null | undefined;
|
|
@@ -5,6 +5,8 @@ import { TASKS_RELATIVE_PATH } from './tasks-file.js';
|
|
|
5
5
|
export const buildAgentSessionBindingMarker = ({ agent, workspace, }) => `Hive session binding: workspace_id=${workspace.id}; agent_id=${agent.id}`;
|
|
6
6
|
export const buildAgentLegacyIdentityMarker = ({ agent, workspace, }) => `You are ${escapeHiveEnvelopeText(agent.name)} (${agent.role}) in workspace ${escapeHiveEnvelopeText(workspace.name)}.`;
|
|
7
7
|
export const WORKER_STARTUP_READY_STATUS = 'ready: startup prompt received; Hive CLI channel OK; waiting for dispatch.';
|
|
8
|
+
export const REVIEWER_STARTUP_READY_STATUS = 'ready: reviewer available as the final review gate. If review finds issues, have them fixed and send the work back to me until no issues remain before reporting completion to the user.';
|
|
9
|
+
const getStartupReadyStatus = (agent) => agent.role === 'reviewer' ? REVIEWER_STARTUP_READY_STATUS : WORKER_STARTUP_READY_STATUS;
|
|
8
10
|
export const buildAgentStartupInstructions = ({ agent, memoryDigest, workspace, flags = FEATURE_FLAGS_ALL_OFF, }) => {
|
|
9
11
|
const { workflowsEnabled } = flags;
|
|
10
12
|
const lines = [
|
|
@@ -35,7 +37,8 @@ export const buildAgentStartupInstructions = ({ agent, memoryDigest, workspace,
|
|
|
35
37
|
lines.push('Available team commands:', '- team status "<finding>" [--artifact <path>] escalate a patrol finding to the Orchestrator', '- team status --stdin [--artifact <path>] same, body read from stdin', '- team recall "<query>" [--limit <n>] [--window <n>] search prior team messages/reports in this workspace', '- team memory show <memory-id> inspect a memory entry and its evidence', '- team memory search "<query>" [--limit <n>] search active workspace memory', '- team --help command syntax only', '', 'You observe only: no dispatches are ever assigned to you, and `team report` is not available to your role.', '', 'Hive sentinel boundaries:', ...getHiveTeamRules(agent, flags));
|
|
36
38
|
}
|
|
37
39
|
else {
|
|
38
|
-
|
|
40
|
+
const startupReadyStatus = getStartupReadyStatus(agent);
|
|
41
|
+
lines.push('Available team commands:', '- team report "<result>" [--dispatch <id>] [--artifact <path>] report done / failed / blocked', '- team report --stdin [--dispatch <id>] [--artifact <path>] same, body read from stdin (multi-line / quotes / special chars)', '- team status "<state>" [--artifact <path>] progress / standby update at any time; never closes a dispatch', '- team status --stdin [--artifact <path>] same, body read from stdin', '- team recall "<query>" [--limit <n>] [--window <n>] search prior team messages/reports in this workspace', '- team memory show <memory-id> inspect a memory entry and its evidence', '- team memory search "<query>" [--limit <n>] search active workspace memory', '- team memory dream show <dream-run-id> inspect a pending Dream run only when the Orchestrator assigns memory review', '- team --help command syntax only; NOT a way to report', '', 'Startup handshake:', `- After reading this startup message, immediately run exactly once: \`team status "${startupReadyStatus}"\`.`, '- This readiness status tells the Orchestrator this member started successfully; it does not close any dispatch.', '- Do not use `team report` for startup readiness. Use `team report` only after you receive an assigned dispatch task.', '', 'Syntax notes:', '- The body is the first positional argument; flag order is free: `team report "result" --dispatch X` and `team report --dispatch X "result"` are both valid.', "- Long bodies (multi-line / quotes / shell metacharacters) always go through `--stdin`, piped in via your shell (POSIX: a quoted heredoc `<<'EOF' … EOF` to stop $var / backtick expansion; Windows cmd: `type body.txt |` or `< body.txt`; PowerShell: `Get-Content -Raw -Encoding utf8 body.txt |`). `--stdin` only reads from stdin and relies on no shell syntax of its own.", '- On error the CLI also prints USAGE — fix the arguments against it.', '', 'Hive member boundaries:', ...getHiveTeamRules(agent, flags));
|
|
39
42
|
}
|
|
40
43
|
lines.push('', '</hive-message>', '');
|
|
41
44
|
return lines.join('\n');
|
|
@@ -23,6 +23,7 @@ export const buildOrchestratorReminderTail = ({ workflowsEnabled }) => {
|
|
|
23
23
|
'(a) `team list`, then `team send "<member-name>" "<task>"` to dispatch to a suitable existing member by name. Prefer user-created/user-managed members already in the current team; stale names fail. ' +
|
|
24
24
|
`If no existing member fits, a member is stopped/queued/not prompt-ready, or independent branches need more members, report that condition and recommend what member the user may start or add; do not take a branch yourself. Use \`team spawn <role> [--cli <${BUILTIN_COMMAND_PRESET_CLI_LIST}>] [--ephemeral]\` only when the user explicitly authorizes it or workspace policy permits autonomous staffing for this task. ` +
|
|
25
25
|
'(b) `team cancel --dispatch <id> "<reason>"` to close an obsolete dispatch. ' +
|
|
26
|
+
'Accepted dispatches should normally be left alone until the member reports. Do not cancel or send status checks unless progress is genuinely overdue or the user asks. ' +
|
|
26
27
|
(workflowsEnabled
|
|
27
28
|
? '(c) `team workflow run --stdin` to fan out across 3+ members or run a staged review→fix — never a loop of `team send` (no barrier, no UI group, no stop button). (d) plain text only for clarification, status/final synthesis, or the explicitly allowed trivial check. '
|
|
28
29
|
: '(c) plain text only for clarification, status/final synthesis, or the explicitly allowed trivial check. ') +
|
|
@@ -58,6 +59,7 @@ const CORE_ORCHESTRATOR_RULES = [
|
|
|
58
59
|
'Do not keep a non-trivial parallel branch for yourself just to avoid dispatching.',
|
|
59
60
|
'If a dispatch must be changed or cancelled, use `team cancel --dispatch <id> "<reason>"`, then resend the full updated task if needed.',
|
|
60
61
|
'Each `team send` creates a separate dispatch and requires a separate `team report`.',
|
|
62
|
+
'Accepted dispatches should normally be left alone until the member reports. Do not cancel or send status checks unless progress is genuinely overdue or the user asks.',
|
|
61
63
|
'Treat stopped, queued, delivery-failed, and prompt-readiness-timeout as current-team runtime conditions to report or retry, not proof that the member is unsuitable.',
|
|
62
64
|
'`team spawn` is persistent by default. When authorized to create task-scoped capacity, prefer `--ephemeral` unless the user asks for persistent capacity.',
|
|
63
65
|
'All Hive members in one workspace share the same filesystem root with no per-member isolation. Split dispatches so no two members edit the same files/modules at the same time; if work would collide, serialize it or assign one owner.',
|
|
@@ -340,6 +340,7 @@ export const teamRoutes = [
|
|
|
340
340
|
status,
|
|
341
341
|
});
|
|
342
342
|
sendJson(response, 202, {
|
|
343
|
+
delivery_state: result.deliveryState,
|
|
343
344
|
dispatch_id: result.dispatch?.id ?? null,
|
|
344
345
|
forward_error: result.forwardError,
|
|
345
346
|
forwarded: result.forwarded,
|
|
@@ -351,6 +352,7 @@ export const teamRoutes = [
|
|
|
351
352
|
else {
|
|
352
353
|
const result = store.reportTask(projectId, fromAgentId, reportInput);
|
|
353
354
|
sendJson(response, 202, {
|
|
355
|
+
delivery_state: result.deliveryState,
|
|
354
356
|
dispatch_id: result.dispatch?.id ?? null,
|
|
355
357
|
forward_error: result.forwardError,
|
|
356
358
|
forwarded: result.forwarded,
|
|
@@ -379,6 +381,7 @@ export const teamRoutes = [
|
|
|
379
381
|
text: resultText,
|
|
380
382
|
});
|
|
381
383
|
sendJson(response, 202, {
|
|
384
|
+
delivery_state: result.deliveryState,
|
|
382
385
|
dispatch_id: result.dispatch?.id ?? null,
|
|
383
386
|
forward_error: result.forwardError,
|
|
384
387
|
forwarded: result.forwarded,
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { normalizeWorkerAvatar } from '../shared/worker-avatar.js';
|
|
2
|
+
import { resolveCommandPresetLaunchConfig, resolveStartupCommandLaunchConfig, } from './agent-launch-resolver.js';
|
|
3
|
+
import { BadRequestError } from './http-errors.js';
|
|
4
|
+
import { autostartAgent } from './orchestrator-autostart.js';
|
|
5
|
+
import { getDefaultRoleDescription } from './role-templates.js';
|
|
6
|
+
import { getRequiredParam, readJsonBody, route, sendJson } from './route-helpers.js';
|
|
7
|
+
import { enrichTeamList } from './team-list-enrichment.js';
|
|
8
|
+
import { serializeTeamListItem } from './team-list-serializer.js';
|
|
9
|
+
import { requireUiTokenFromRequest } from './ui-auth-helpers.js';
|
|
10
|
+
import { resolveWorkspaceUiLanguage } from './workspace-ui-language.js';
|
|
11
|
+
const getRuntimePort = (request) => String(request.socket.localPort ?? '');
|
|
12
|
+
const readWorkerAvatar = (value) => {
|
|
13
|
+
try {
|
|
14
|
+
return normalizeWorkerAvatar(value);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw new BadRequestError(error instanceof Error ? error.message : String(error));
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const getSerializedWorker = (workspaceId, workerId, store) => {
|
|
21
|
+
const worker = store.listWorkers(workspaceId).find((item) => item.id === workerId);
|
|
22
|
+
if (!worker) {
|
|
23
|
+
throw new Error(`Worker not found: ${workerId}`);
|
|
24
|
+
}
|
|
25
|
+
const [enriched] = enrichTeamList(workspaceId, store, [worker]);
|
|
26
|
+
if (!enriched)
|
|
27
|
+
throw new Error(`Worker enrichment failed: ${workerId}`);
|
|
28
|
+
return serializeTeamListItem(enriched, undefined, { includeAvatar: true });
|
|
29
|
+
};
|
|
30
|
+
export const workerRoutes = [
|
|
31
|
+
route('POST', '/api/workspaces/:workspaceId/workers', async ({ params, request, response, store }) => {
|
|
32
|
+
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id is required');
|
|
33
|
+
if (!workspaceId) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
|
|
37
|
+
const body = await readJsonBody(request);
|
|
38
|
+
const avatar = Object.hasOwn(body, 'avatar') ? readWorkerAvatar(body.avatar) : null;
|
|
39
|
+
const presetId = body.command_preset_id ?? null;
|
|
40
|
+
const startupCommand = typeof body.startup_command === 'string' ? body.startup_command : null;
|
|
41
|
+
const language = resolveWorkspaceUiLanguage(store.settings, workspaceId, body.ui_language);
|
|
42
|
+
const launchConfig = startupCommand?.trim()
|
|
43
|
+
? resolveStartupCommandLaunchConfig(store.settings, startupCommand, presetId)
|
|
44
|
+
: presetId
|
|
45
|
+
? resolveCommandPresetLaunchConfig(store.settings, presetId)
|
|
46
|
+
: undefined;
|
|
47
|
+
if (presetId && !startupCommand?.trim() && !launchConfig) {
|
|
48
|
+
throw new Error(`Command preset not found: ${presetId}`);
|
|
49
|
+
}
|
|
50
|
+
const worker = store.addWorker(workspaceId, {
|
|
51
|
+
...body,
|
|
52
|
+
avatar,
|
|
53
|
+
description: typeof body.description === 'string'
|
|
54
|
+
? body.description
|
|
55
|
+
: getDefaultRoleDescription(body.role, language),
|
|
56
|
+
});
|
|
57
|
+
if (launchConfig) {
|
|
58
|
+
try {
|
|
59
|
+
store.configureAgentLaunch(workspaceId, worker.id, launchConfig);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
store.deleteWorker(workspaceId, worker.id);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const agentStart = body.autostart === true
|
|
67
|
+
? await autostartAgent(store, workspaceId, worker.id, getRuntimePort(request), {
|
|
68
|
+
missingConfigError: 'No worker launch config available',
|
|
69
|
+
})
|
|
70
|
+
: { ok: false, error: null, run_id: null };
|
|
71
|
+
sendJson(response, 201, {
|
|
72
|
+
...getSerializedWorker(workspaceId, worker.id, store),
|
|
73
|
+
agent_start: agentStart,
|
|
74
|
+
});
|
|
75
|
+
}),
|
|
76
|
+
route('DELETE', '/api/workspaces/:workspaceId/workers/:workerId', ({ params, request, response, store }) => {
|
|
77
|
+
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id and worker id are required');
|
|
78
|
+
const workerId = getRequiredParam(response, params, 'workerId', 'Workspace id and worker id are required');
|
|
79
|
+
if (!workspaceId || !workerId) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
|
|
83
|
+
store.deleteWorker(workspaceId, workerId);
|
|
84
|
+
response.statusCode = 204;
|
|
85
|
+
response.end();
|
|
86
|
+
}),
|
|
87
|
+
route('PATCH', '/api/workspaces/:workspaceId/workers/:workerId', async ({ params, request, response, store }) => {
|
|
88
|
+
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id and worker id are required');
|
|
89
|
+
const workerId = getRequiredParam(response, params, 'workerId', 'Workspace id and worker id are required');
|
|
90
|
+
if (!workspaceId || !workerId) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
|
|
94
|
+
const body = await readJsonBody(request);
|
|
95
|
+
const hasName = Object.hasOwn(body, 'name');
|
|
96
|
+
const hasAvatar = Object.hasOwn(body, 'avatar');
|
|
97
|
+
if (!hasName && !hasAvatar) {
|
|
98
|
+
sendJson(response, 400, { error: 'name or avatar is required' });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (hasName && typeof body.name !== 'string') {
|
|
102
|
+
sendJson(response, 400, { error: 'name must be a string' });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const avatar = hasAvatar ? readWorkerAvatar(body.avatar) : undefined;
|
|
106
|
+
store.updateWorkerProfile(workspaceId, workerId, {
|
|
107
|
+
...(hasName ? { name: body.name } : {}),
|
|
108
|
+
...(hasAvatar ? { avatar: avatar ?? null } : {}),
|
|
109
|
+
});
|
|
110
|
+
sendJson(response, 200, getSerializedWorker(workspaceId, workerId, store));
|
|
111
|
+
}),
|
|
112
|
+
];
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { resolveCommandPresetLaunchConfig, resolveStartupCommandLaunchConfig, } from './agent-launch-resolver.js';
|
|
2
1
|
import { PtyInactiveError } from './http-errors.js';
|
|
3
|
-
import {
|
|
2
|
+
import { autostartOrchestrator } from './orchestrator-autostart.js';
|
|
4
3
|
import { seedOrchestratorLaunchConfig } from './orchestrator-launch.js';
|
|
5
|
-
import { getDefaultRoleDescription } from './role-templates.js';
|
|
6
4
|
import { getRequiredParam, readJsonBody, route, sendJson } from './route-helpers.js';
|
|
7
5
|
import { authenticateCliAgent, requireCommandForRole } from './team-authz.js';
|
|
8
6
|
import { enrichTeamList } from './team-list-enrichment.js';
|
|
@@ -15,7 +13,7 @@ import { resolveWorkspaceUiLanguage, writeWorkspaceUiLanguage } from './workspac
|
|
|
15
13
|
into the team list payload. Display-only ages — no timeout or heartbeat is
|
|
16
14
|
derived from them; `queued` rows age from createdAt (submittedAt is null
|
|
17
15
|
until delivery). */
|
|
18
|
-
const serializeTeamListWithOpenDispatches = (store, workspaceId) => {
|
|
16
|
+
const serializeTeamListWithOpenDispatches = (store, workspaceId, options = {}) => {
|
|
19
17
|
const now = Date.now();
|
|
20
18
|
const workflowAgentId = getWorkflowAgentId(workspaceId);
|
|
21
19
|
const openByWorker = new Map();
|
|
@@ -35,17 +33,7 @@ const serializeTeamListWithOpenDispatches = (store, workspaceId) => {
|
|
|
35
33
|
});
|
|
36
34
|
openByWorker.set(dispatch.toAgentId, list);
|
|
37
35
|
}
|
|
38
|
-
return enrichTeamList(workspaceId, store, store.listWorkers(workspaceId)).map((worker) => serializeTeamListItem(worker, openByWorker.get(worker.id)));
|
|
39
|
-
};
|
|
40
|
-
const getSerializedWorker = (workspaceId, workerId, store) => {
|
|
41
|
-
const worker = store.listWorkers(workspaceId).find((item) => item.id === workerId);
|
|
42
|
-
if (!worker) {
|
|
43
|
-
throw new Error(`Worker not found: ${workerId}`);
|
|
44
|
-
}
|
|
45
|
-
const [enriched] = enrichTeamList(workspaceId, store, [worker]);
|
|
46
|
-
if (!enriched)
|
|
47
|
-
throw new Error(`Worker enrichment failed: ${workerId}`);
|
|
48
|
-
return serializeTeamListItem(enriched);
|
|
36
|
+
return enrichTeamList(workspaceId, store, store.listWorkers(workspaceId)).map((worker) => serializeTeamListItem(worker, openByWorker.get(worker.id), options));
|
|
49
37
|
};
|
|
50
38
|
const getRuntimePort = (request) => String(request.socket.localPort ?? '');
|
|
51
39
|
export const workspaceRoutes = [
|
|
@@ -92,7 +80,7 @@ export const workspaceRoutes = [
|
|
|
92
80
|
return;
|
|
93
81
|
}
|
|
94
82
|
requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
|
|
95
|
-
sendJson(response, 200, serializeTeamListWithOpenDispatches(store, workspaceId));
|
|
83
|
+
sendJson(response, 200, serializeTeamListWithOpenDispatches(store, workspaceId, { includeAvatar: true }));
|
|
96
84
|
}),
|
|
97
85
|
route('GET', '/api/workspaces/:workspaceId/team', ({ params, request, response, store }) => {
|
|
98
86
|
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id is required');
|
|
@@ -114,75 +102,6 @@ export const workspaceRoutes = [
|
|
|
114
102
|
store.drainReportOutbox(workspaceId, agent.id);
|
|
115
103
|
sendJson(response, 200, serializeTeamListWithOpenDispatches(store, workspaceId));
|
|
116
104
|
}),
|
|
117
|
-
route('POST', '/api/workspaces/:workspaceId/workers', async ({ params, request, response, store }) => {
|
|
118
|
-
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id is required');
|
|
119
|
-
if (!workspaceId) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
|
|
123
|
-
const body = await readJsonBody(request);
|
|
124
|
-
const presetId = body.command_preset_id ?? null;
|
|
125
|
-
const startupCommand = typeof body.startup_command === 'string' ? body.startup_command : null;
|
|
126
|
-
const language = resolveWorkspaceUiLanguage(store.settings, workspaceId, body.ui_language);
|
|
127
|
-
const launchConfig = startupCommand?.trim()
|
|
128
|
-
? resolveStartupCommandLaunchConfig(store.settings, startupCommand, presetId)
|
|
129
|
-
: presetId
|
|
130
|
-
? resolveCommandPresetLaunchConfig(store.settings, presetId)
|
|
131
|
-
: undefined;
|
|
132
|
-
if (presetId && !startupCommand?.trim() && !launchConfig) {
|
|
133
|
-
throw new Error(`Command preset not found: ${presetId}`);
|
|
134
|
-
}
|
|
135
|
-
const worker = store.addWorker(workspaceId, {
|
|
136
|
-
...body,
|
|
137
|
-
description: typeof body.description === 'string'
|
|
138
|
-
? body.description
|
|
139
|
-
: getDefaultRoleDescription(body.role, language),
|
|
140
|
-
});
|
|
141
|
-
if (launchConfig) {
|
|
142
|
-
try {
|
|
143
|
-
store.configureAgentLaunch(workspaceId, worker.id, launchConfig);
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
store.deleteWorker(workspaceId, worker.id);
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const agentStart = body.autostart === true
|
|
151
|
-
? await autostartAgent(store, workspaceId, worker.id, getRuntimePort(request), {
|
|
152
|
-
missingConfigError: 'No worker launch config available',
|
|
153
|
-
})
|
|
154
|
-
: { ok: false, error: null, run_id: null };
|
|
155
|
-
sendJson(response, 201, {
|
|
156
|
-
...getSerializedWorker(workspaceId, worker.id, store),
|
|
157
|
-
agent_start: agentStart,
|
|
158
|
-
});
|
|
159
|
-
}),
|
|
160
|
-
route('DELETE', '/api/workspaces/:workspaceId/workers/:workerId', ({ params, request, response, store }) => {
|
|
161
|
-
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id and worker id are required');
|
|
162
|
-
const workerId = getRequiredParam(response, params, 'workerId', 'Workspace id and worker id are required');
|
|
163
|
-
if (!workspaceId || !workerId) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
|
|
167
|
-
store.deleteWorker(workspaceId, workerId);
|
|
168
|
-
response.statusCode = 204;
|
|
169
|
-
response.end();
|
|
170
|
-
}),
|
|
171
|
-
route('PATCH', '/api/workspaces/:workspaceId/workers/:workerId', async ({ params, request, response, store }) => {
|
|
172
|
-
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id and worker id are required');
|
|
173
|
-
const workerId = getRequiredParam(response, params, 'workerId', 'Workspace id and worker id are required');
|
|
174
|
-
if (!workspaceId || !workerId) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
|
|
178
|
-
const body = await readJsonBody(request);
|
|
179
|
-
if (typeof body.name !== 'string') {
|
|
180
|
-
sendJson(response, 400, { error: 'name is required' });
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
store.renameWorker(workspaceId, workerId, body.name);
|
|
184
|
-
sendJson(response, 200, getSerializedWorker(workspaceId, workerId, store));
|
|
185
|
-
}),
|
|
186
105
|
route('POST', '/api/workspaces/:workspaceId/user-input', async ({ params, request, response, store }) => {
|
|
187
106
|
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id is required');
|
|
188
107
|
if (!workspaceId) {
|
|
@@ -17,6 +17,7 @@ import { teamMemoryRoutes } from './routes-team-memory.js';
|
|
|
17
17
|
import { teamRecallRoutes } from './routes-team-recall.js';
|
|
18
18
|
import { uiRoutes } from './routes-ui.js';
|
|
19
19
|
import { versionRoutes } from './routes-version.js';
|
|
20
|
+
import { workerRoutes } from './routes-workers.js';
|
|
20
21
|
import { workflowScheduleRoutes } from './routes-workflow-schedules.js';
|
|
21
22
|
import { workflowRoutes } from './routes-workflows.js';
|
|
22
23
|
import { workspaceMemoryRoutes } from './routes-workspace-memory.js';
|
|
@@ -25,6 +26,7 @@ import { workspaceUploadRoutes } from './routes-workspace-uploads.js';
|
|
|
25
26
|
import { workspaceRoutes } from './routes-workspaces.js';
|
|
26
27
|
const routes = [
|
|
27
28
|
...workspaceRoutes,
|
|
29
|
+
...workerRoutes,
|
|
28
30
|
...scenarioRoutes,
|
|
29
31
|
...workspaceUploadRoutes,
|
|
30
32
|
...openWorkspaceRoutes,
|
|
@@ -34,6 +34,11 @@ export interface RuntimeStore {
|
|
|
34
34
|
addWorker: (workspaceId: string, input: WorkerInput) => AgentSummary;
|
|
35
35
|
addWorkerWithLaunch: (workspaceId: string, input: WorkerInput, launchConfig: AgentLaunchConfigInput) => AgentSummary;
|
|
36
36
|
deleteWorker: (workspaceId: string, workerId: string) => void;
|
|
37
|
+
updateWorkerProfile: (workspaceId: string, workerId: string, input: {
|
|
38
|
+
avatar?: string | null;
|
|
39
|
+
name?: string;
|
|
40
|
+
}) => AgentSummary;
|
|
41
|
+
updateWorkerAvatar: (workspaceId: string, workerId: string, avatar: string | null) => AgentSummary;
|
|
37
42
|
renameWorker: (workspaceId: string, workerId: string, name: string) => AgentSummary;
|
|
38
43
|
recordUserInput: (workspaceId: string, orchestratorId: string, text: string) => void;
|
|
39
44
|
deliverUserInput: (workspaceId: string, orchestratorId: string, text: string) => Promise<void>;
|
|
@@ -81,6 +81,8 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
81
81
|
},
|
|
82
82
|
addWorker: (workspaceId, input) => services.workspaceStore.addWorker(workspaceId, input),
|
|
83
83
|
addWorkerWithLaunch: workerMutations.addWorkerWithLaunch,
|
|
84
|
+
updateWorkerProfile: (workspaceId, workerId, input) => services.workspaceStore.updateWorkerProfile(workspaceId, workerId, input),
|
|
85
|
+
updateWorkerAvatar: (workspaceId, workerId, avatar) => services.workspaceStore.updateWorkerAvatar(workspaceId, workerId, avatar),
|
|
84
86
|
renameWorker: (workspaceId, workerId, name) => services.workspaceStore.renameWorker(workspaceId, workerId, name),
|
|
85
87
|
deleteWorker: workerMutations.deleteWorker,
|
|
86
88
|
recordUserInput: services.teamOps.recordUserInput,
|
|
@@ -31,7 +31,8 @@ import { applySchemaVersion35 } from './sqlite-schema-v35.js';
|
|
|
31
31
|
import { applySchemaVersion36 } from './sqlite-schema-v36.js';
|
|
32
32
|
import { applySchemaVersion37 } from './sqlite-schema-v37.js';
|
|
33
33
|
import { applySchemaVersion38 } from './sqlite-schema-v38.js';
|
|
34
|
-
|
|
34
|
+
import { applySchemaVersion39 } from './sqlite-schema-v39.js';
|
|
35
|
+
export const CURRENT_SCHEMA_VERSION = 39;
|
|
35
36
|
// Idempotent column-add helper. SQLite doesn't have `ALTER TABLE … ADD COLUMN
|
|
36
37
|
// IF NOT EXISTS`, so PRAGMA-check first. Safe to call on every init; required
|
|
37
38
|
// for foreign-built DBs where the version-gated migration is skipped.
|
|
@@ -60,6 +61,7 @@ export const initializeRuntimeDatabase = (db) => {
|
|
|
60
61
|
workspace_id TEXT NOT NULL,
|
|
61
62
|
name TEXT NOT NULL,
|
|
62
63
|
description TEXT,
|
|
64
|
+
avatar TEXT,
|
|
63
65
|
last_session_id TEXT,
|
|
64
66
|
role TEXT NOT NULL,
|
|
65
67
|
created_at INTEGER NOT NULL
|
|
@@ -239,6 +241,7 @@ export const initializeRuntimeDatabase = (db) => {
|
|
|
239
241
|
// migration leaves these absent. PRAGMA + ALTER here makes the writes safe.
|
|
240
242
|
ensureColumn(db, 'workers', 'ephemeral', 'INTEGER NOT NULL DEFAULT 0');
|
|
241
243
|
ensureColumn(db, 'workers', 'spawned_by', 'TEXT');
|
|
244
|
+
ensureColumn(db, 'workers', 'avatar', 'TEXT');
|
|
242
245
|
ensureColumn(db, 'dispatches', 'workflow_run_id', 'TEXT');
|
|
243
246
|
ensureColumn(db, 'dispatches', 'step_index', 'INTEGER');
|
|
244
247
|
// M9 — phase + label on dispatches so the workflow UI can render the
|
|
@@ -449,4 +452,8 @@ export const initializeRuntimeDatabase = (db) => {
|
|
|
449
452
|
if (!appliedVersions.has(38)) {
|
|
450
453
|
db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)').run(38, Date.now());
|
|
451
454
|
}
|
|
455
|
+
applySchemaVersion39(db);
|
|
456
|
+
if (!appliedVersions.has(39)) {
|
|
457
|
+
db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)').run(39, Date.now());
|
|
458
|
+
}
|
|
452
459
|
};
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import type { TeamListItem, TeamListItemPayload, TeamListOpenDispatchPayload } from '../shared/types.js';
|
|
2
|
-
|
|
2
|
+
type SerializeTeamListItemOptions = {
|
|
3
|
+
includeAvatar?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare const serializeTeamListItem: ({ avatar, commandPresetId, ephemeral, id, lastPtyLine, name, pendingTaskCount, role, spawnedBy, status, }: TeamListItem, openDispatches?: TeamListOpenDispatchPayload[], options?: SerializeTeamListItemOptions) => TeamListItemPayload;
|
|
6
|
+
export {};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export const serializeTeamListItem = ({ commandPresetId, ephemeral, id, lastPtyLine, name, pendingTaskCount, role, spawnedBy, status, }, openDispatches) => ({
|
|
1
|
+
export const serializeTeamListItem = ({ avatar, commandPresetId, ephemeral, id, lastPtyLine, name, pendingTaskCount, role, spawnedBy, status, }, openDispatches, options = {}) => ({
|
|
2
2
|
id,
|
|
3
3
|
name,
|
|
4
4
|
role,
|
|
5
5
|
status,
|
|
6
6
|
pending_task_count: pendingTaskCount,
|
|
7
|
+
...(options.includeAvatar === true && avatar ? { avatar } : {}),
|
|
7
8
|
last_pty_line: lastPtyLine ?? null,
|
|
8
9
|
command_preset_id: commandPresetId ?? null,
|
|
9
10
|
...(ephemeral === true ? { ephemeral: true } : {}),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DREAM_MEMORY_BODY_MAX_CHARS, DREAM_MEMORY_MAX_ADDS_PER_RUN, MEMORY_PROCEDURE_REF_ID_MAX_CHARS, MEMORY_PROCEDURE_REF_TITLE_MAX_CHARS, MEMORY_TAG_MAX_CHARS, MEMORY_TAG_MAX_COUNT, } from '../shared/team-memory.js';
|
|
1
2
|
import { escapeHiveEnvelopeAttribute, escapeHiveEnvelopeText } from './hive-envelope-escape.js';
|
|
2
3
|
export const DREAM_PROMPT_MESSAGE_LIMIT = 200;
|
|
3
4
|
export const DREAM_PROMPT_MEMORY_LIMIT = 100;
|
|
@@ -38,14 +39,25 @@ export const buildDreamPrompt = (input) => [
|
|
|
38
39
|
'Read only the protocol messages and memory entries below.',
|
|
39
40
|
'Return strict JSON only, with shape {"ops":[...]} and no prose.',
|
|
40
41
|
'',
|
|
42
|
+
'Output contract (runtime-validated):',
|
|
43
|
+
'- Return exactly {"ops":[...]} as JSON. No markdown, no prose.',
|
|
44
|
+
`- Add no more than ${DREAM_MEMORY_MAX_ADDS_PER_RUN} memories per run.`,
|
|
45
|
+
`- For add/rewrite/merge, body is required and must be <= ${DREAM_MEMORY_BODY_MAX_CHARS} characters. Prefer one compact sentence.`,
|
|
46
|
+
`- If any proposed body would exceed ${DREAM_MEMORY_BODY_MAX_CHARS} characters, shorten it before returning JSON. Never emit an over-limit body.`,
|
|
47
|
+
`- tags must be an array of <= ${MEMORY_TAG_MAX_COUNT} non-empty strings, each <= ${MEMORY_TAG_MAX_CHARS} characters.`,
|
|
48
|
+
'- confidence, when present, must be a number from 0 to 1.',
|
|
49
|
+
'- sources, when present, must be [{"sequence": <positive integer>}] from the protocol window.',
|
|
50
|
+
`- procedure_ref.id must be <= ${MEMORY_PROCEDURE_REF_ID_MAX_CHARS} characters; procedure_ref.title, when present, must be <= ${MEMORY_PROCEDURE_REF_TITLE_MAX_CHARS} characters.`,
|
|
51
|
+
'- For rewrite/merge, omit kind, tags, confidence, and procedure_ref to preserve current values. Use procedure_ref:null only when intentionally clearing an existing ref; provide a structured procedure_ref when the resulting kind is procedure_ref.',
|
|
52
|
+
'',
|
|
41
53
|
'Allowed ops:',
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
`- {"op":"add","kind":"fact|preference|decision|pitfall","body":"<=${DREAM_MEMORY_BODY_MAX_CHARS} chars","tags":[],"confidence":0.0-1.0,"sources":[{"sequence":123}]}`,
|
|
55
|
+
`- {"op":"add","kind":"procedure_ref","body":"<=${DREAM_MEMORY_BODY_MAX_CHARS} chars","tags":[],"confidence":0.0-1.0,"sources":[{"sequence":123}],"procedure_ref":{"type":"workflow|skill|procedure|template|doc","id":"<=${MEMORY_PROCEDURE_REF_ID_MAX_CHARS} chars","title":"<=${MEMORY_PROCEDURE_REF_TITLE_MAX_CHARS} chars"}}`,
|
|
56
|
+
`- {"op":"rewrite","id":"<memory-id>","body":"<=${DREAM_MEMORY_BODY_MAX_CHARS} chars"}`,
|
|
45
57
|
'- {"op":"archive","id":"<memory-id>","reason":"..."}',
|
|
46
|
-
|
|
58
|
+
`- {"op":"merge","into":"<memory-id>","from":["<memory-id>"],"body":"<=${DREAM_MEMORY_BODY_MAX_CHARS} chars"}`,
|
|
47
59
|
'',
|
|
48
|
-
|
|
60
|
+
`Rules: add at most ${DREAM_MEMORY_MAX_ADDS_PER_RUN} entries; archive never deletes; if nothing is worth remembering, return {"ops":[]}.`,
|
|
49
61
|
'Do not use kind "procedure_ref" for ordinary workflow advice, command sequences, or procedural lessons. Use fact, decision, preference, or pitfall instead.',
|
|
50
62
|
'Use kind "procedure_ref" only when the memory points to a durable existing workflow/skill/procedure/template/doc identity, and always include procedure_ref.type plus procedure_ref.id. Keep body as a short reason to consult it.',
|
|
51
63
|
'Use protocol evidence only: report text, dispatch status, user input, and artifacts. Do not trust self-praise without a concrete report or failure signal.',
|
|
@@ -88,6 +100,7 @@ export const buildDreamMaintenancePayload = (run) => [
|
|
|
88
100
|
'- Members may run team memory dream show only when you explicitly assign Dream review; only the orchestrator may run team memory apply.',
|
|
89
101
|
'- Do not edit .hive/memory.md directly and do not use team memory add/forget for this Dream run.',
|
|
90
102
|
'- If nothing is worth remembering, apply {"ops":[]}.',
|
|
103
|
+
`- Follow the Dream output contract from dream show; every submitted body must be ${DREAM_MEMORY_BODY_MAX_CHARS} characters or fewer.`,
|
|
91
104
|
'- The runtime will validate the run id, workspace, source window, operation shape, and touched memory ids transactionally.',
|
|
92
105
|
'',
|
|
93
106
|
'</hive-system-memory-maintenance>',
|
|
@@ -88,7 +88,9 @@ export interface CancelTaskInput {
|
|
|
88
88
|
fromAgentId: string;
|
|
89
89
|
reason: string;
|
|
90
90
|
}
|
|
91
|
+
export type ReportDeliveryState = 'delivered' | 'delivering' | 'queued' | 'failed';
|
|
91
92
|
export interface ReportTaskResult {
|
|
93
|
+
deliveryState?: ReportDeliveryState;
|
|
92
94
|
dispatch: DispatchRecord | null;
|
|
93
95
|
forwardError: string | null;
|
|
94
96
|
forwarded: boolean;
|
|
@@ -121,11 +123,13 @@ export declare const createTeamOperations: ({ agentRuntime, createDispatch, dele
|
|
|
121
123
|
dispatch: null;
|
|
122
124
|
forwardError: string | null;
|
|
123
125
|
forwarded: boolean;
|
|
126
|
+
deliveryState?: ReportDeliveryState;
|
|
124
127
|
};
|
|
125
128
|
reportTask(workspaceId: string, workerId: string, input?: ReportTaskInput): {
|
|
126
129
|
pendingWarning?: string;
|
|
127
130
|
dispatch: DispatchRecord;
|
|
128
131
|
forwardError: string | null;
|
|
129
132
|
forwarded: boolean;
|
|
133
|
+
deliveryState?: ReportDeliveryState;
|
|
130
134
|
};
|
|
131
135
|
};
|