@tt-a1i/hive 2.1.10 → 2.1.11
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 +18 -0
- package/dist/src/cli/hive-mcp.d.ts +62 -0
- package/dist/src/cli/hive-mcp.js +253 -0
- package/dist/src/cli/hive.js +8 -0
- package/dist/src/cli/team.d.ts +8 -0
- package/dist/src/cli/team.js +100 -0
- package/dist/src/server/agent-runtime-contract.d.ts +1 -0
- package/dist/src/server/agent-runtime.js +4 -0
- package/dist/src/server/agent-startup-instructions.js +1 -1
- package/dist/src/server/diagnostics-support-bundle.d.ts +1 -1
- package/dist/src/server/external-goal-auth.d.ts +1 -0
- package/dist/src/server/external-goal-auth.js +1 -0
- package/dist/src/server/external-goal-bridge.d.ts +103 -0
- package/dist/src/server/external-goal-bridge.js +299 -0
- package/dist/src/server/external-goal-store.d.ts +59 -0
- package/dist/src/server/external-goal-store.js +165 -0
- package/dist/src/server/hive-team-guidance.js +1 -0
- package/dist/src/server/post-start-input-writer.js +23 -6
- package/dist/src/server/route-types.d.ts +30 -0
- package/dist/src/server/routes-external-goals.d.ts +2 -0
- package/dist/src/server/routes-external-goals.js +184 -0
- package/dist/src/server/routes-runtime.js +6 -1
- package/dist/src/server/routes-team-goals.d.ts +2 -0
- package/dist/src/server/routes-team-goals.js +60 -0
- package/dist/src/server/routes.js +4 -0
- package/dist/src/server/runtime-store-contract.d.ts +11 -0
- package/dist/src/server/runtime-store-external-goals.d.ts +47 -0
- package/dist/src/server/runtime-store-external-goals.js +24 -0
- package/dist/src/server/runtime-store-helpers.d.ts +3 -0
- package/dist/src/server/runtime-store-helpers.js +11 -1
- package/dist/src/server/runtime-store.js +6 -0
- package/dist/src/server/sqlite-schema-v37.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v37.js +38 -0
- package/dist/src/server/sqlite-schema.d.ts +1 -1
- package/dist/src/server/sqlite-schema.js +41 -1
- package/dist/src/server/team-authz.d.ts +1 -1
- package/dist/src/server/team-authz.js +1 -0
- package/dist/src/server/ui-auth.d.ts +2 -0
- package/dist/src/server/ui-auth.js +7 -0
- package/package.json +1 -1
- package/web/dist/assets/{AddWorkerDialog-Dc0slXtx.js → AddWorkerDialog-CMQdmZJQ.js} +2 -2
- package/web/dist/assets/{AddWorkspaceFlow-AyzSdx5w.js → AddWorkspaceFlow-arGy-LVX.js} +1 -1
- package/web/dist/assets/{FirstRunWizard-DGB5zWhl.js → FirstRunWizard-PRHGFYry.js} +1 -1
- package/web/dist/assets/{MarketplaceDrawer-DAZ-crqq.js → MarketplaceDrawer-Dm-Z-YL8.js} +1 -1
- package/web/dist/assets/{TaskGraphDrawer-DYk-0uKD.js → TaskGraphDrawer-HaGRlpar.js} +1 -1
- package/web/dist/assets/{WhatsNewDialog-B5tu3MV2.js → WhatsNewDialog-9upxbs9b.js} +1 -1
- package/web/dist/assets/{WorkerModal-C73K7SUg.js → WorkerModal-DAC0fORb.js} +1 -1
- package/web/dist/assets/{WorkflowsDrawer-mrSAPqAh.js → WorkflowsDrawer-Dqv4vNZW.js} +1 -1
- package/web/dist/assets/{WorkspaceMemoryDrawer-CCz3HB4F.js → WorkspaceMemoryDrawer-QvVUM6qp.js} +1 -1
- package/web/dist/assets/{WorkspaceTaskDrawer-C7JzoRp8.js → WorkspaceTaskDrawer-CdiraIBo.js} +1 -1
- package/web/dist/assets/index-B1vvo0-M.js +84 -0
- package/web/dist/assets/{search-Bx1xfpzr.js → search-DgMNPsfM.js} +1 -1
- package/web/dist/assets/{square-terminal-Dfr9QC1I.js → square-terminal-CiDDFh5Z.js} +1 -1
- package/web/dist/index.html +1 -1
- package/web/dist/sw.js +1 -1
- package/web/dist/assets/index-CVQc8PLJ.js +0 -84
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { HIVE_SUPERVISOR_TOKEN_HEADER } from './external-goal-auth.js';
|
|
2
|
+
import { ExternalGoalDeliveryError } from './external-goal-bridge.js';
|
|
3
|
+
import { BadRequestError, ForbiddenError } from './http-errors.js';
|
|
4
|
+
import { getRequiredParam, readJsonBody, route, sendJson } from './route-helpers.js';
|
|
5
|
+
const BODY_MAX_CHARS = 40_000;
|
|
6
|
+
const SOURCE_MAX_CHARS = 80;
|
|
7
|
+
const requireNonEmptyString = (value, field, maxChars = BODY_MAX_CHARS) => {
|
|
8
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
9
|
+
throw new BadRequestError(`Missing ${field}`);
|
|
10
|
+
}
|
|
11
|
+
if ([...value].length > maxChars) {
|
|
12
|
+
throw new BadRequestError(`${field} must be ${maxChars} characters or fewer`);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
};
|
|
16
|
+
const optionalNonEmptyString = (value, field, maxChars = SOURCE_MAX_CHARS) => {
|
|
17
|
+
if (value === undefined || value === null)
|
|
18
|
+
return undefined;
|
|
19
|
+
return requireNonEmptyString(value, field, maxChars);
|
|
20
|
+
};
|
|
21
|
+
const serializeEvent = (event) => ({
|
|
22
|
+
artifacts: event.artifacts,
|
|
23
|
+
body: event.body,
|
|
24
|
+
created_at: event.createdAt,
|
|
25
|
+
goal_id: event.goalId,
|
|
26
|
+
id: event.id,
|
|
27
|
+
kind: event.kind,
|
|
28
|
+
sequence: event.sequence,
|
|
29
|
+
status: event.status,
|
|
30
|
+
workspace_id: event.workspaceId,
|
|
31
|
+
});
|
|
32
|
+
const serializeSession = (session) => ({
|
|
33
|
+
closed_at: session.closedAt,
|
|
34
|
+
created_at: session.createdAt,
|
|
35
|
+
goal: session.goal,
|
|
36
|
+
id: session.id,
|
|
37
|
+
source: session.source,
|
|
38
|
+
status: session.status,
|
|
39
|
+
summary: session.summary,
|
|
40
|
+
title: session.title,
|
|
41
|
+
updated_at: session.updatedAt,
|
|
42
|
+
workspace_id: session.workspaceId,
|
|
43
|
+
});
|
|
44
|
+
const serializeWaitResult = (result) => ({
|
|
45
|
+
cursor: result.cursor,
|
|
46
|
+
events: result.events.map(serializeEvent),
|
|
47
|
+
goal_id: result.goalId,
|
|
48
|
+
status: result.status,
|
|
49
|
+
});
|
|
50
|
+
const sendDeliveryError = (response, error) => {
|
|
51
|
+
sendJson(response, error.statusCode, {
|
|
52
|
+
cursor: error.cursor,
|
|
53
|
+
error: error.message,
|
|
54
|
+
goal_id: error.goalId,
|
|
55
|
+
status: error.status,
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
const requireExternalController = (store, request) => {
|
|
59
|
+
if (store.authorizeRemoteTunnelRequest(request)) {
|
|
60
|
+
throw new ForbiddenError('Supervisor token is not available over the remote tunnel');
|
|
61
|
+
}
|
|
62
|
+
const rawToken = request.headers[HIVE_SUPERVISOR_TOKEN_HEADER];
|
|
63
|
+
const token = Array.isArray(rawToken) ? rawToken[0] : rawToken;
|
|
64
|
+
if (!store.validateSupervisorToken(token)) {
|
|
65
|
+
throw new ForbiddenError('External goal endpoint requires valid Supervisor token');
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
export const externalGoalRoutes = [
|
|
69
|
+
route('GET', '/api/external-goals/session', ({ request, response, store }) => {
|
|
70
|
+
if (store.authorizeRemoteTunnelRequest(request)) {
|
|
71
|
+
throw new ForbiddenError('Supervisor token is not available over the remote tunnel');
|
|
72
|
+
}
|
|
73
|
+
sendJson(response, 200, {
|
|
74
|
+
token: store.getSupervisorToken(),
|
|
75
|
+
token_type: 'hive-supervisor',
|
|
76
|
+
});
|
|
77
|
+
}),
|
|
78
|
+
route('GET', '/api/external-goals/workspaces', ({ request, response, store }) => {
|
|
79
|
+
requireExternalController(store, request);
|
|
80
|
+
sendJson(response, 200, { workspaces: store.listExternalGoalWorkspaces() });
|
|
81
|
+
}),
|
|
82
|
+
route('GET', '/api/external-goals/workspaces/:workspaceId', ({ params, request, response, store }) => {
|
|
83
|
+
requireExternalController(store, request);
|
|
84
|
+
const workspaceId = getRequiredParam(response, params, 'workspaceId', 'Workspace id is required');
|
|
85
|
+
if (!workspaceId)
|
|
86
|
+
return;
|
|
87
|
+
sendJson(response, 200, store.inspectExternalGoalWorkspace({ workspaceId }));
|
|
88
|
+
}),
|
|
89
|
+
route('POST', '/api/external-goals/start', async ({ request, response, store }) => {
|
|
90
|
+
requireExternalController(store, request);
|
|
91
|
+
const body = await readJsonBody(request);
|
|
92
|
+
const workspaceId = requireNonEmptyString(body.workspace_id, 'workspace_id', 200);
|
|
93
|
+
const goal = requireNonEmptyString(body.goal, 'goal');
|
|
94
|
+
const source = optionalNonEmptyString(body.source, 'source') ?? 'hive-mcp';
|
|
95
|
+
let result;
|
|
96
|
+
try {
|
|
97
|
+
result = await store.startExternalGoal({
|
|
98
|
+
workspaceId,
|
|
99
|
+
goal,
|
|
100
|
+
source,
|
|
101
|
+
...(body.context !== undefined ? { context: body.context } : {}),
|
|
102
|
+
...(body.timeout_hint_ms !== undefined ? { timeoutHintMs: body.timeout_hint_ms } : {}),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (error instanceof ExternalGoalDeliveryError) {
|
|
107
|
+
sendDeliveryError(response, error);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
sendJson(response, 202, {
|
|
113
|
+
cursor: result.cursor,
|
|
114
|
+
events: result.events.map(serializeEvent),
|
|
115
|
+
goal_id: result.goalId,
|
|
116
|
+
ok: true,
|
|
117
|
+
session: serializeSession(result.session),
|
|
118
|
+
status: result.status,
|
|
119
|
+
});
|
|
120
|
+
}),
|
|
121
|
+
route('POST', '/api/external-goals/wait', async ({ request, response, store }) => {
|
|
122
|
+
requireExternalController(store, request);
|
|
123
|
+
const body = await readJsonBody(request);
|
|
124
|
+
const goalId = requireNonEmptyString(body.goal_id, 'goal_id', 200);
|
|
125
|
+
const result = await store.waitExternalGoal({
|
|
126
|
+
goalId,
|
|
127
|
+
...(body.cursor !== undefined ? { cursor: body.cursor } : {}),
|
|
128
|
+
...(body.timeout_ms !== undefined ? { timeoutMs: body.timeout_ms } : {}),
|
|
129
|
+
});
|
|
130
|
+
sendJson(response, 200, serializeWaitResult(result));
|
|
131
|
+
}),
|
|
132
|
+
route('POST', '/api/external-goals/continue', async ({ request, response, store }) => {
|
|
133
|
+
requireExternalController(store, request);
|
|
134
|
+
const body = await readJsonBody(request);
|
|
135
|
+
const goalId = requireNonEmptyString(body.goal_id, 'goal_id', 200);
|
|
136
|
+
const message = requireNonEmptyString(body.message, 'message');
|
|
137
|
+
let result;
|
|
138
|
+
try {
|
|
139
|
+
result = await store.continueExternalGoal({
|
|
140
|
+
goalId,
|
|
141
|
+
message,
|
|
142
|
+
...(body.context !== undefined ? { context: body.context } : {}),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (error instanceof ExternalGoalDeliveryError) {
|
|
147
|
+
sendDeliveryError(response, error);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
sendJson(response, 202, {
|
|
153
|
+
cursor: result.cursor,
|
|
154
|
+
event: serializeEvent(result.event),
|
|
155
|
+
ok: true,
|
|
156
|
+
session: serializeSession(result.session),
|
|
157
|
+
status: result.status,
|
|
158
|
+
});
|
|
159
|
+
}),
|
|
160
|
+
route('POST', '/api/external-goals/cancel', async ({ request, response, store }) => {
|
|
161
|
+
requireExternalController(store, request);
|
|
162
|
+
const body = await readJsonBody(request);
|
|
163
|
+
const goalId = requireNonEmptyString(body.goal_id, 'goal_id', 200);
|
|
164
|
+
const reason = requireNonEmptyString(body.reason, 'reason');
|
|
165
|
+
let result;
|
|
166
|
+
try {
|
|
167
|
+
result = await store.cancelExternalGoal({ goalId, reason });
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (error instanceof ExternalGoalDeliveryError) {
|
|
171
|
+
sendDeliveryError(response, error);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
sendJson(response, 202, {
|
|
177
|
+
cursor: result.cursor,
|
|
178
|
+
event: serializeEvent(result.event),
|
|
179
|
+
ok: true,
|
|
180
|
+
session: serializeSession(result.session),
|
|
181
|
+
status: result.status,
|
|
182
|
+
});
|
|
183
|
+
}),
|
|
184
|
+
];
|
|
@@ -74,6 +74,11 @@ export const runtimeRoutes = [
|
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
76
|
requireUiTokenFromRequest(request, store.validateUiToken, store.authorizeRemoteTunnelRequest);
|
|
77
|
-
|
|
77
|
+
const run = store.findLiveRun(runId);
|
|
78
|
+
if (!run) {
|
|
79
|
+
sendJson(response, 404, { error: 'Run not found' });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
sendJson(response, 200, serializeLiveAgentRun(run));
|
|
78
83
|
}),
|
|
79
84
|
];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { isExternalGoalReportStatus } from './external-goal-store.js';
|
|
2
|
+
import { BadRequestError } from './http-errors.js';
|
|
3
|
+
import { readJsonBody, route, sendJson } from './route-helpers.js';
|
|
4
|
+
import { authenticateCliAgent, requireCommandForRole } from './team-authz.js';
|
|
5
|
+
const BODY_MAX_CHARS = 40_000;
|
|
6
|
+
const requireNonEmptyString = (value, field, maxChars = BODY_MAX_CHARS) => {
|
|
7
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
8
|
+
throw new BadRequestError(`Missing ${field}`);
|
|
9
|
+
}
|
|
10
|
+
if ([...value].length > maxChars) {
|
|
11
|
+
throw new BadRequestError(`${field} must be ${maxChars} characters or fewer`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
};
|
|
15
|
+
const getArtifacts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
16
|
+
export const teamGoalRoutes = [
|
|
17
|
+
route('POST', '/api/team/goal/report', async ({ request, response, store }) => {
|
|
18
|
+
const body = await readJsonBody(request);
|
|
19
|
+
const projectId = requireNonEmptyString(body.project_id, 'project_id', 200);
|
|
20
|
+
const fromAgentId = requireNonEmptyString(body.from_agent_id, 'from_agent_id', 200);
|
|
21
|
+
const goalId = requireNonEmptyString(body.goal_id, 'goal_id', 200);
|
|
22
|
+
const resultText = requireNonEmptyString(body.result, 'result');
|
|
23
|
+
if (!isExternalGoalReportStatus(body.status)) {
|
|
24
|
+
throw new BadRequestError('Invalid status; expected progress, done, blocked, or failed');
|
|
25
|
+
}
|
|
26
|
+
const agent = authenticateCliAgent({
|
|
27
|
+
fromAgentId,
|
|
28
|
+
getAgent: store.getAgent,
|
|
29
|
+
token: body.token,
|
|
30
|
+
validateToken: store.validateAgentToken,
|
|
31
|
+
workspaceId: projectId,
|
|
32
|
+
});
|
|
33
|
+
requireCommandForRole(agent, 'goal_report');
|
|
34
|
+
const result = store.reportExternalGoal({
|
|
35
|
+
artifacts: getArtifacts(body.artifacts),
|
|
36
|
+
body: resultText,
|
|
37
|
+
fromAgentId,
|
|
38
|
+
goalId,
|
|
39
|
+
status: body.status,
|
|
40
|
+
workspaceId: projectId,
|
|
41
|
+
});
|
|
42
|
+
sendJson(response, 202, {
|
|
43
|
+
cursor: result.cursor,
|
|
44
|
+
event: {
|
|
45
|
+
artifacts: result.event.artifacts,
|
|
46
|
+
body: result.event.body,
|
|
47
|
+
created_at: result.event.createdAt,
|
|
48
|
+
goal_id: result.event.goalId,
|
|
49
|
+
id: result.event.id,
|
|
50
|
+
kind: result.event.kind,
|
|
51
|
+
sequence: result.event.sequence,
|
|
52
|
+
status: result.event.status,
|
|
53
|
+
workspace_id: result.event.workspaceId,
|
|
54
|
+
},
|
|
55
|
+
goal_id: goalId,
|
|
56
|
+
ok: true,
|
|
57
|
+
status: result.status,
|
|
58
|
+
});
|
|
59
|
+
}),
|
|
60
|
+
];
|
|
@@ -2,6 +2,7 @@ import { matchPath } from './route-helpers.js';
|
|
|
2
2
|
import { actionCenterRoutes } from './routes-action-center.js';
|
|
3
3
|
import { diagnosticsRoutes } from './routes-diagnostics.js';
|
|
4
4
|
import { dispatchRoutes } from './routes-dispatches.js';
|
|
5
|
+
import { externalGoalRoutes } from './routes-external-goals.js';
|
|
5
6
|
import { fsRoutes } from './routes-fs.js';
|
|
6
7
|
import { marketplaceRoutes } from './routes-marketplace.js';
|
|
7
8
|
import { openWorkspaceRoutes } from './routes-open-workspace.js';
|
|
@@ -11,6 +12,7 @@ import { scenarioRoutes } from './routes-scenarios.js';
|
|
|
11
12
|
import { settingsRoutes } from './routes-settings.js';
|
|
12
13
|
import { taskRoutes } from './routes-tasks.js';
|
|
13
14
|
import { teamRoutes } from './routes-team.js';
|
|
15
|
+
import { teamGoalRoutes } from './routes-team-goals.js';
|
|
14
16
|
import { teamMemoryRoutes } from './routes-team-memory.js';
|
|
15
17
|
import { teamRecallRoutes } from './routes-team-recall.js';
|
|
16
18
|
import { uiRoutes } from './routes-ui.js';
|
|
@@ -36,8 +38,10 @@ const routes = [
|
|
|
36
38
|
...workspaceMemoryDreamRoutes,
|
|
37
39
|
...workspaceMemoryRoutes,
|
|
38
40
|
...runtimeRoutes,
|
|
41
|
+
...externalGoalRoutes,
|
|
39
42
|
...teamRecallRoutes,
|
|
40
43
|
...teamMemoryRoutes,
|
|
44
|
+
...teamGoalRoutes,
|
|
41
45
|
...teamRoutes,
|
|
42
46
|
...fsRoutes,
|
|
43
47
|
...marketplaceRoutes,
|
|
@@ -4,6 +4,7 @@ import type { AgentManager } from './agent-manager.js';
|
|
|
4
4
|
import type { AgentLaunchConfigInput, PersistedAgentRun } from './agent-run-store.js';
|
|
5
5
|
import type { LiveAgentRun } from './agent-runtime-types.js';
|
|
6
6
|
import type { DispatchRecord, ListDispatchesOptions } from './dispatch-ledger-store.js';
|
|
7
|
+
import type { ExternalGoalBridge, ExternalGoalCancelInput, ExternalGoalContinueInput, ExternalGoalReportInput, ExternalGoalStartInput, ExternalGoalWaitInput } from './external-goal-bridge.js';
|
|
7
8
|
import type { RecoveryMessage } from './message-log-store.js';
|
|
8
9
|
import type { PtyOutputBus } from './pty-output-bus.js';
|
|
9
10
|
import type { RemoteAuditStore } from './remote-audit-store.js';
|
|
@@ -50,6 +51,13 @@ export interface RuntimeStore {
|
|
|
50
51
|
listDispatches: (workspaceId: string, options?: ListDispatchesOptions) => DispatchRecord[];
|
|
51
52
|
listOpenDispatches: (workspaceId: string) => DispatchRecord[];
|
|
52
53
|
listRecentDispatches: (workspaceId: string, limit?: number) => DispatchRecord[];
|
|
54
|
+
listExternalGoalWorkspaces: ExternalGoalBridge['listWorkspaces'];
|
|
55
|
+
inspectExternalGoalWorkspace: ExternalGoalBridge['inspectWorkspace'];
|
|
56
|
+
startExternalGoal: (input: ExternalGoalStartInput) => ReturnType<ExternalGoalBridge['startGoal']>;
|
|
57
|
+
continueExternalGoal: (input: ExternalGoalContinueInput) => ReturnType<ExternalGoalBridge['continueGoal']>;
|
|
58
|
+
reportExternalGoal: (input: ExternalGoalReportInput) => ReturnType<ExternalGoalBridge['reportGoal']>;
|
|
59
|
+
waitExternalGoal: (input: ExternalGoalWaitInput) => ReturnType<ExternalGoalBridge['waitGoal']>;
|
|
60
|
+
cancelExternalGoal: (input: ExternalGoalCancelInput) => ReturnType<ExternalGoalBridge['cancelGoal']>;
|
|
53
61
|
listWorkers: (workspaceId: string) => TeamListItem[];
|
|
54
62
|
getLastPtyLineForAgent: (workspaceId: string, agentId: string) => string | null;
|
|
55
63
|
getWorkspaceSnapshot: (workspaceId: string) => WorkspaceRecord;
|
|
@@ -70,6 +78,7 @@ export interface RuntimeStore {
|
|
|
70
78
|
workspace_id: string;
|
|
71
79
|
}>>;
|
|
72
80
|
startWorkspaceWatch: (workspaceId: string) => Promise<void>;
|
|
81
|
+
findLiveRun: (runId: string) => LiveAgentRun | undefined;
|
|
73
82
|
getLiveRun: (runId: string) => LiveAgentRun;
|
|
74
83
|
waitForRunExit: (runId: string, timeoutMs: number) => Promise<boolean>;
|
|
75
84
|
getActiveRunByAgentId: (workspaceId: string, agentId: string) => LiveAgentRun | undefined;
|
|
@@ -102,9 +111,11 @@ export interface RuntimeStore {
|
|
|
102
111
|
settings: SettingsStore;
|
|
103
112
|
writeRunInput: (runId: string, input: Buffer | string) => void;
|
|
104
113
|
getUiToken: () => string;
|
|
114
|
+
getSupervisorToken: () => string;
|
|
105
115
|
stopAgentRun: (runId: string) => void;
|
|
106
116
|
validateAgentToken: (agentId: string, token: string | undefined) => boolean;
|
|
107
117
|
validateUiToken: (token: string | undefined) => boolean;
|
|
118
|
+
validateSupervisorToken: (token: string | undefined) => boolean;
|
|
108
119
|
authorizeRemoteTunnelRequest: (request: IncomingMessage) => boolean;
|
|
109
120
|
getRemoteTunnelSecret: () => string;
|
|
110
121
|
getRemoteAuditStore: () => RemoteAuditStore;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { RuntimeStoreServices } from './runtime-store-helpers.js';
|
|
2
|
+
export declare const createRuntimeStoreExternalGoalMethods: (services: RuntimeStoreServices) => {
|
|
3
|
+
cancelExternalGoal: (input: import("./external-goal-bridge.js").ExternalGoalCancelInput) => Promise<{
|
|
4
|
+
cursor: number;
|
|
5
|
+
event: import("./external-goal-store.js").ExternalGoalEvent;
|
|
6
|
+
session: import("./external-goal-store.js").ExternalGoalSession;
|
|
7
|
+
status: "open" | "cancelled" | "in_progress" | "blocked" | "done" | "failed";
|
|
8
|
+
}>;
|
|
9
|
+
continueExternalGoal: (input: import("./external-goal-bridge.js").ExternalGoalContinueInput) => Promise<{
|
|
10
|
+
cursor: number;
|
|
11
|
+
event: import("./external-goal-store.js").ExternalGoalEvent;
|
|
12
|
+
session: import("./external-goal-store.js").ExternalGoalSession;
|
|
13
|
+
status: "open" | "cancelled" | "in_progress" | "blocked" | "done" | "failed";
|
|
14
|
+
}>;
|
|
15
|
+
inspectExternalGoalWorkspace: (input: {
|
|
16
|
+
workspaceId: string;
|
|
17
|
+
}) => {
|
|
18
|
+
workspace: import("../shared/types.js").WorkspaceSummary;
|
|
19
|
+
orchestrator: {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
status: "idle" | "working" | "stopped";
|
|
23
|
+
active_run: boolean;
|
|
24
|
+
};
|
|
25
|
+
members: import("../shared/types.js").TeamListItemPayload[];
|
|
26
|
+
};
|
|
27
|
+
listExternalGoalWorkspaces: () => import("../shared/types.js").WorkspaceSummary[];
|
|
28
|
+
reportExternalGoal: (input: import("./external-goal-bridge.js").ExternalGoalReportInput) => {
|
|
29
|
+
cursor: number;
|
|
30
|
+
event: import("./external-goal-store.js").ExternalGoalEvent;
|
|
31
|
+
session: import("./external-goal-store.js").ExternalGoalSession;
|
|
32
|
+
status: "open" | "cancelled" | "in_progress" | "blocked" | "done" | "failed";
|
|
33
|
+
};
|
|
34
|
+
startExternalGoal: (input: import("./external-goal-bridge.js").ExternalGoalStartInput) => Promise<{
|
|
35
|
+
cursor: number;
|
|
36
|
+
events: import("./external-goal-store.js").ExternalGoalEvent[];
|
|
37
|
+
goalId: string;
|
|
38
|
+
session: import("./external-goal-store.js").ExternalGoalSession;
|
|
39
|
+
status: "open" | "cancelled" | "in_progress" | "blocked" | "done" | "failed";
|
|
40
|
+
}>;
|
|
41
|
+
waitExternalGoal: (input: import("./external-goal-bridge.js").ExternalGoalWaitInput) => Promise<{
|
|
42
|
+
cursor: number;
|
|
43
|
+
events: import("./external-goal-store.js").ExternalGoalEvent[];
|
|
44
|
+
goalId: string;
|
|
45
|
+
status: "open" | "cancelled" | "in_progress" | "blocked" | "done" | "failed";
|
|
46
|
+
}>;
|
|
47
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createExternalGoalBridge } from './external-goal-bridge.js';
|
|
2
|
+
import { getOrchestratorId } from './workspace-store-support.js';
|
|
3
|
+
export const createRuntimeStoreExternalGoalMethods = (services) => {
|
|
4
|
+
const bridge = createExternalGoalBridge({
|
|
5
|
+
deliverToOrchestrator: (workspaceId, text) => services.agentRuntime.deliverSystemMessageToAgent(workspaceId, getOrchestratorId(workspaceId), text, {
|
|
6
|
+
requireActiveRun: true,
|
|
7
|
+
}),
|
|
8
|
+
getActiveRunByAgentId: (workspaceId, agentId) => services.agentRuntime.getActiveRunByAgentId(workspaceId, agentId),
|
|
9
|
+
getAgent: (workspaceId, agentId) => services.workspaceStore.getAgent(workspaceId, agentId),
|
|
10
|
+
getWorkspaceSnapshot: (workspaceId) => services.workspaceStore.getWorkspaceSnapshot(workspaceId),
|
|
11
|
+
goalStore: services.externalGoalStore,
|
|
12
|
+
listWorkers: (workspaceId) => services.workspaceStore.listWorkers(workspaceId),
|
|
13
|
+
listWorkspaces: () => services.workspaceStore.listWorkspaces(),
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
cancelExternalGoal: bridge.cancelGoal,
|
|
17
|
+
continueExternalGoal: bridge.continueGoal,
|
|
18
|
+
inspectExternalGoalWorkspace: bridge.inspectWorkspace,
|
|
19
|
+
listExternalGoalWorkspaces: bridge.listWorkspaces,
|
|
20
|
+
reportExternalGoal: bridge.reportGoal,
|
|
21
|
+
startExternalGoal: bridge.startGoal,
|
|
22
|
+
waitExternalGoal: bridge.waitGoal,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
@@ -3,6 +3,7 @@ import { type AgentLaunchConfigInput, createAgentRunStore } from './agent-run-st
|
|
|
3
3
|
import { createAgentRuntime } from './agent-runtime.js';
|
|
4
4
|
import type { LiveAgentRun } from './agent-runtime-types.js';
|
|
5
5
|
import { createDispatchLedgerStore } from './dispatch-ledger-store.js';
|
|
6
|
+
import { createExternalGoalStore } from './external-goal-store.js';
|
|
6
7
|
import { createMessageLogStore } from './message-log-store.js';
|
|
7
8
|
import { createProtocolEventStats } from './protocol-event-stats.js';
|
|
8
9
|
import type { PtyOutputBus } from './pty-output-bus.js';
|
|
@@ -40,6 +41,7 @@ export interface RuntimeStoreServices {
|
|
|
40
41
|
agentRuntime: ReturnType<typeof createAgentRuntime>;
|
|
41
42
|
db: ReturnType<typeof openRuntimeDatabase>;
|
|
42
43
|
dispatchLedgerStore: ReturnType<typeof createDispatchLedgerStore>;
|
|
44
|
+
externalGoalStore: ReturnType<typeof createExternalGoalStore>;
|
|
43
45
|
isRuntimeClosing: () => boolean;
|
|
44
46
|
markRuntimeClosing: () => void;
|
|
45
47
|
messageLogStore: ReturnType<typeof createMessageLogStore>;
|
|
@@ -91,6 +93,7 @@ export declare const createRuntimeStoreLifecycle: ({ agentManager, services, }:
|
|
|
91
93
|
peekAgentLaunchConfig: (workspaceId: string, agentId: string) => AgentLaunchConfigInput | undefined;
|
|
92
94
|
deleteWorkspaceShell: (workspaceId: string) => void;
|
|
93
95
|
closeWorkspaceShell: (workspaceId: string, runId: string) => boolean;
|
|
96
|
+
findLiveRun: (runId: string) => LiveAgentRun | undefined;
|
|
94
97
|
getLiveRun: (runId: string) => LiveAgentRun;
|
|
95
98
|
waitForRunExit: (runId: string, timeoutMs: number) => Promise<boolean>;
|
|
96
99
|
getPtyOutputBus: () => PtyOutputBus;
|
|
@@ -5,6 +5,7 @@ import { createAgentRunStore } from './agent-run-store.js';
|
|
|
5
5
|
import { createAgentRuntime } from './agent-runtime.js';
|
|
6
6
|
import { createAgentSessionStore } from './agent-session-store.js';
|
|
7
7
|
import { createDispatchLedgerStore } from './dispatch-ledger-store.js';
|
|
8
|
+
import { createExternalGoalStore } from './external-goal-store.js';
|
|
8
9
|
import { readFeatureFlags } from './feature-flags.js';
|
|
9
10
|
import { createMessageLogStore } from './message-log-store.js';
|
|
10
11
|
import { seedOrchestratorLaunchConfig } from './orchestrator-launch.js';
|
|
@@ -80,6 +81,7 @@ export const createRuntimeStoreServices = (options = {}) => {
|
|
|
80
81
|
const uploadStorage = createWorkspaceUploadStorage(options.dataDir);
|
|
81
82
|
const messageLogStore = createMessageLogStore(db);
|
|
82
83
|
const dispatchLedgerStore = createDispatchLedgerStore(db);
|
|
84
|
+
const externalGoalStore = createExternalGoalStore(db);
|
|
83
85
|
const teamMemoryStore = createTeamMemoryStore(db);
|
|
84
86
|
const teamMemoryDreamStore = createTeamMemoryDreamStore(db);
|
|
85
87
|
const teamRecallStore = createTeamRecallStore(db);
|
|
@@ -305,6 +307,7 @@ export const createRuntimeStoreServices = (options = {}) => {
|
|
|
305
307
|
agentRuntime,
|
|
306
308
|
db,
|
|
307
309
|
dispatchLedgerStore,
|
|
310
|
+
externalGoalStore,
|
|
308
311
|
isRuntimeClosing: () => closing,
|
|
309
312
|
markRuntimeClosing: () => {
|
|
310
313
|
closing = true;
|
|
@@ -426,6 +429,7 @@ export const createRuntimeStoreLifecycle = ({ agentManager, services, }) => {
|
|
|
426
429
|
});
|
|
427
430
|
return Promise.all(starts);
|
|
428
431
|
};
|
|
432
|
+
const findLiveRun = (runId) => services.shellRuntime.getLiveRun(runId) ?? services.agentRuntime.findLiveRun(runId);
|
|
429
433
|
return {
|
|
430
434
|
close: async () => {
|
|
431
435
|
services.markRuntimeClosing();
|
|
@@ -457,7 +461,13 @@ export const createRuntimeStoreLifecycle = ({ agentManager, services, }) => {
|
|
|
457
461
|
services.shellRuntime.deleteWorkspace(workspaceId);
|
|
458
462
|
},
|
|
459
463
|
closeWorkspaceShell: (workspaceId, runId) => services.shellRuntime.closeRun(workspaceId, runId),
|
|
460
|
-
|
|
464
|
+
findLiveRun,
|
|
465
|
+
getLiveRun: (runId) => {
|
|
466
|
+
const run = findLiveRun(runId);
|
|
467
|
+
if (!run)
|
|
468
|
+
throw new Error(`Live run not found: ${runId}`);
|
|
469
|
+
return run;
|
|
470
|
+
},
|
|
461
471
|
waitForRunExit: (runId, timeoutMs) => services.agentRuntime.waitForRunExit(runId, timeoutMs),
|
|
462
472
|
getPtyOutputBus: () => {
|
|
463
473
|
if (!agentManager)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRuntimeStoreDreamMethods } from './runtime-store-dream.js';
|
|
2
|
+
import { createRuntimeStoreExternalGoalMethods } from './runtime-store-external-goals.js';
|
|
2
3
|
import { createRuntimeStoreLifecycle, createRuntimeStoreServices, logTasksFileWatchStartError, } from './runtime-store-helpers.js';
|
|
3
4
|
import { createRuntimeStoreMemoryMethods } from './runtime-store-memory.js';
|
|
4
5
|
import { createRuntimeStoreRemoteMethods } from './runtime-store-remote.js';
|
|
@@ -61,6 +62,7 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
61
62
|
try {
|
|
62
63
|
runDataMutation(() => {
|
|
63
64
|
services.dispatchLedgerStore.deleteWorkspaceDispatches(workspaceId);
|
|
65
|
+
services.externalGoalStore.deleteWorkspaceGoals(workspaceId);
|
|
64
66
|
services.teamMemoryStore.deleteWorkspaceMemories(workspaceId);
|
|
65
67
|
services.teamMemoryDreamStore.deleteWorkspaceDreamRuns(workspaceId);
|
|
66
68
|
services.workspaceStore.deleteWorkspaceData(workspaceId);
|
|
@@ -92,6 +94,7 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
92
94
|
listDispatches: services.dispatchLedgerStore.listWorkspaceDispatches,
|
|
93
95
|
listOpenDispatches: services.dispatchLedgerStore.listOpenWorkspaceDispatches,
|
|
94
96
|
listRecentDispatches: services.dispatchLedgerStore.listRecentWorkspaceDispatches,
|
|
97
|
+
...createRuntimeStoreExternalGoalMethods(services),
|
|
95
98
|
listWorkers: (workspaceId) => services.workspaceStore.listWorkers(workspaceId),
|
|
96
99
|
getLastPtyLineForAgent: (workspaceId, agentId) => services.workerOutputTracker?.getLastPtyLine(workspaceId, agentId) ?? null,
|
|
97
100
|
getWorkspaceSnapshot: (workspaceId) => services.workspaceStore.getWorkspaceSnapshot(workspaceId),
|
|
@@ -106,6 +109,7 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
106
109
|
autostartConfiguredAgents: lifecycle.autostartConfiguredAgents,
|
|
107
110
|
startWorkspaceWatch: lifecycle.startWorkspaceWatch,
|
|
108
111
|
startWorkspaceShell: lifecycle.startWorkspaceShell,
|
|
112
|
+
findLiveRun: lifecycle.findLiveRun,
|
|
109
113
|
getLiveRun: lifecycle.getLiveRun,
|
|
110
114
|
waitForRunExit: lifecycle.waitForRunExit,
|
|
111
115
|
getActiveRunByAgentId: (workspaceId, agentId) => services.agentRuntime.getActiveRunByAgentId(workspaceId, agentId),
|
|
@@ -122,9 +126,11 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
122
126
|
settings: services.settings,
|
|
123
127
|
writeRunInput: lifecycle.writeRunInput,
|
|
124
128
|
getUiToken: () => services.uiAuth.getToken(),
|
|
129
|
+
getSupervisorToken: () => services.uiAuth.getSupervisorToken(),
|
|
125
130
|
stopAgentRun: lifecycle.stopTerminalRun,
|
|
126
131
|
validateAgentToken: (agentId, token) => services.agentRuntime.validateAgentToken(agentId, token),
|
|
127
132
|
validateUiToken: (token) => services.uiAuth.validate(token),
|
|
133
|
+
validateSupervisorToken: (token) => services.uiAuth.validateSupervisorToken(token),
|
|
128
134
|
getRetentionSignals: () => services.protocolEventStats.getRetentionSignals(),
|
|
129
135
|
...createRuntimeStoreRemoteMethods(services),
|
|
130
136
|
getWorkflowDispatchAwaiter: () => services.workflowDispatchAwaiter,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const applySchemaVersion37 = (db) => {
|
|
2
|
+
db.exec(`
|
|
3
|
+
CREATE TABLE IF NOT EXISTS external_goal_sessions (
|
|
4
|
+
id TEXT PRIMARY KEY,
|
|
5
|
+
workspace_id TEXT NOT NULL,
|
|
6
|
+
source TEXT NOT NULL,
|
|
7
|
+
status TEXT NOT NULL,
|
|
8
|
+
goal TEXT NOT NULL,
|
|
9
|
+
context_json TEXT NOT NULL,
|
|
10
|
+
title TEXT,
|
|
11
|
+
summary TEXT,
|
|
12
|
+
created_at INTEGER NOT NULL,
|
|
13
|
+
updated_at INTEGER NOT NULL,
|
|
14
|
+
closed_at INTEGER
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_external_goal_sessions_workspace
|
|
18
|
+
ON external_goal_sessions (workspace_id, created_at);
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS external_goal_events (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
goal_id TEXT NOT NULL,
|
|
23
|
+
workspace_id TEXT NOT NULL,
|
|
24
|
+
sequence INTEGER NOT NULL,
|
|
25
|
+
kind TEXT NOT NULL,
|
|
26
|
+
status TEXT,
|
|
27
|
+
body TEXT NOT NULL,
|
|
28
|
+
artifacts_json TEXT NOT NULL,
|
|
29
|
+
created_at INTEGER NOT NULL,
|
|
30
|
+
UNIQUE(goal_id, sequence)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_external_goal_events_goal
|
|
34
|
+
ON external_goal_events (goal_id, sequence);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_external_goal_events_workspace
|
|
36
|
+
ON external_goal_events (workspace_id, created_at);
|
|
37
|
+
`);
|
|
38
|
+
};
|
|
@@ -29,7 +29,8 @@ import { applySchemaVersion33 } from './sqlite-schema-v33.js';
|
|
|
29
29
|
import { applySchemaVersion34 } from './sqlite-schema-v34.js';
|
|
30
30
|
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
|
+
export const CURRENT_SCHEMA_VERSION = 37;
|
|
33
34
|
// Idempotent column-add helper. SQLite doesn't have `ALTER TABLE … ADD COLUMN
|
|
34
35
|
// IF NOT EXISTS`, so PRAGMA-check first. Safe to call on every init; required
|
|
35
36
|
// for foreign-built DBs where the version-gated migration is skipped.
|
|
@@ -195,6 +196,41 @@ export const initializeRuntimeDatabase = (db) => {
|
|
|
195
196
|
|
|
196
197
|
CREATE INDEX IF NOT EXISTS idx_workspace_uploads_workspace_created
|
|
197
198
|
ON workspace_uploads (workspace_id, created_at DESC, id DESC);
|
|
199
|
+
|
|
200
|
+
CREATE TABLE IF NOT EXISTS external_goal_sessions (
|
|
201
|
+
id TEXT PRIMARY KEY,
|
|
202
|
+
workspace_id TEXT NOT NULL,
|
|
203
|
+
source TEXT NOT NULL,
|
|
204
|
+
status TEXT NOT NULL,
|
|
205
|
+
goal TEXT NOT NULL,
|
|
206
|
+
context_json TEXT NOT NULL,
|
|
207
|
+
title TEXT,
|
|
208
|
+
summary TEXT,
|
|
209
|
+
created_at INTEGER NOT NULL,
|
|
210
|
+
updated_at INTEGER NOT NULL,
|
|
211
|
+
closed_at INTEGER
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
CREATE INDEX IF NOT EXISTS idx_external_goal_sessions_workspace
|
|
215
|
+
ON external_goal_sessions (workspace_id, created_at);
|
|
216
|
+
|
|
217
|
+
CREATE TABLE IF NOT EXISTS external_goal_events (
|
|
218
|
+
id TEXT PRIMARY KEY,
|
|
219
|
+
goal_id TEXT NOT NULL,
|
|
220
|
+
workspace_id TEXT NOT NULL,
|
|
221
|
+
sequence INTEGER NOT NULL,
|
|
222
|
+
kind TEXT NOT NULL,
|
|
223
|
+
status TEXT,
|
|
224
|
+
body TEXT NOT NULL,
|
|
225
|
+
artifacts_json TEXT NOT NULL,
|
|
226
|
+
created_at INTEGER NOT NULL,
|
|
227
|
+
UNIQUE(goal_id, sequence)
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
CREATE INDEX IF NOT EXISTS idx_external_goal_events_goal
|
|
231
|
+
ON external_goal_events (goal_id, sequence);
|
|
232
|
+
CREATE INDEX IF NOT EXISTS idx_external_goal_events_workspace
|
|
233
|
+
ON external_goal_events (workspace_id, created_at);
|
|
198
234
|
`);
|
|
199
235
|
// Idempotent column additions — run on every init regardless of
|
|
200
236
|
// schema_version. The v19 migration adds these columns but is gated on
|
|
@@ -404,4 +440,8 @@ export const initializeRuntimeDatabase = (db) => {
|
|
|
404
440
|
if (!appliedVersions.has(36)) {
|
|
405
441
|
db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)').run(36, Date.now());
|
|
406
442
|
}
|
|
443
|
+
applySchemaVersion37(db);
|
|
444
|
+
if (!appliedVersions.has(37)) {
|
|
445
|
+
db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)').run(37, Date.now());
|
|
446
|
+
}
|
|
407
447
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentSummary } from '../shared/types.js';
|
|
2
|
-
export type TeamCommand = 'send' | 'list' | 'next' | 'report' | 'recall' | 'memory_add' | 'memory_apply' | 'memory_dream_show' | 'memory_forget' | 'memory_search' | 'memory_show' | 'status' | 'cancel' | 'help' | 'spawn' | 'dismiss' | 'workflow';
|
|
2
|
+
export type TeamCommand = 'send' | 'list' | 'next' | 'report' | 'recall' | 'memory_add' | 'memory_apply' | 'memory_dream_show' | 'memory_forget' | 'memory_search' | 'memory_show' | 'status' | 'cancel' | 'help' | 'spawn' | 'dismiss' | 'workflow' | 'goal_report';
|
|
3
3
|
export declare const commandAllowedForRole: (role: AgentSummary["role"], command: TeamCommand) => boolean;
|
|
4
4
|
/**
|
|
5
5
|
* Dispatch-target gate: the sentinel is an observer, so handing it a dispatch
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
2
|
export interface UiAuth {
|
|
3
3
|
getToken: () => string;
|
|
4
|
+
getSupervisorToken: () => string;
|
|
4
5
|
validate: (token: string | undefined) => boolean;
|
|
6
|
+
validateSupervisorToken: (token: string | undefined) => boolean;
|
|
5
7
|
isTunnelRequest: (request: IncomingMessage) => boolean;
|
|
6
8
|
getTunnelSecret: () => string;
|
|
7
9
|
}
|