@tt-a1i/hive 2.1.9 → 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.
Files changed (63) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/src/cli/hive-mcp.d.ts +62 -0
  3. package/dist/src/cli/hive-mcp.js +253 -0
  4. package/dist/src/cli/hive.js +8 -0
  5. package/dist/src/cli/team.d.ts +8 -0
  6. package/dist/src/cli/team.js +100 -0
  7. package/dist/src/server/agent-runtime-contract.d.ts +1 -0
  8. package/dist/src/server/agent-runtime.js +4 -0
  9. package/dist/src/server/agent-startup-instructions.js +1 -1
  10. package/dist/src/server/agent-stdin-dispatcher.js +1 -0
  11. package/dist/src/server/diagnostics-support-bundle.d.ts +1 -1
  12. package/dist/src/server/external-goal-auth.d.ts +1 -0
  13. package/dist/src/server/external-goal-auth.js +1 -0
  14. package/dist/src/server/external-goal-bridge.d.ts +103 -0
  15. package/dist/src/server/external-goal-bridge.js +299 -0
  16. package/dist/src/server/external-goal-store.d.ts +59 -0
  17. package/dist/src/server/external-goal-store.js +165 -0
  18. package/dist/src/server/hive-team-guidance.js +1 -0
  19. package/dist/src/server/post-start-input-writer.js +52 -6
  20. package/dist/src/server/route-types.d.ts +30 -0
  21. package/dist/src/server/routes-external-goals.d.ts +2 -0
  22. package/dist/src/server/routes-external-goals.js +184 -0
  23. package/dist/src/server/routes-runtime.js +6 -1
  24. package/dist/src/server/routes-scenarios.d.ts +12 -17
  25. package/dist/src/server/routes-scenarios.js +41 -17
  26. package/dist/src/server/routes-team-goals.d.ts +2 -0
  27. package/dist/src/server/routes-team-goals.js +60 -0
  28. package/dist/src/server/routes.js +4 -0
  29. package/dist/src/server/runtime-store-contract.d.ts +11 -0
  30. package/dist/src/server/runtime-store-external-goals.d.ts +47 -0
  31. package/dist/src/server/runtime-store-external-goals.js +24 -0
  32. package/dist/src/server/runtime-store-helpers.d.ts +3 -0
  33. package/dist/src/server/runtime-store-helpers.js +11 -1
  34. package/dist/src/server/runtime-store.js +6 -0
  35. package/dist/src/server/scenario-presets.d.ts +7 -4
  36. package/dist/src/server/scenario-presets.js +16 -6
  37. package/dist/src/server/sqlite-schema-v37.d.ts +2 -0
  38. package/dist/src/server/sqlite-schema-v37.js +38 -0
  39. package/dist/src/server/sqlite-schema.d.ts +1 -1
  40. package/dist/src/server/sqlite-schema.js +41 -1
  41. package/dist/src/server/team-authz.d.ts +1 -1
  42. package/dist/src/server/team-authz.js +1 -0
  43. package/dist/src/server/ui-auth.d.ts +2 -0
  44. package/dist/src/server/ui-auth.js +7 -0
  45. package/dist/src/shared/random-worker-name.d.ts +9 -0
  46. package/dist/src/shared/random-worker-name.js +382 -0
  47. package/package.json +1 -1
  48. package/web/dist/assets/{AddWorkerDialog-QBN0m0eH.js → AddWorkerDialog-CMQdmZJQ.js} +2 -2
  49. package/web/dist/assets/{AddWorkspaceFlow-BJy-W2XI.js → AddWorkspaceFlow-arGy-LVX.js} +1 -1
  50. package/web/dist/assets/{FirstRunWizard-V-idMQB7.js → FirstRunWizard-PRHGFYry.js} +1 -1
  51. package/web/dist/assets/{MarketplaceDrawer-D4ldgvSM.js → MarketplaceDrawer-Dm-Z-YL8.js} +1 -1
  52. package/web/dist/assets/{TaskGraphDrawer-BSxRbr1h.js → TaskGraphDrawer-HaGRlpar.js} +1 -1
  53. package/web/dist/assets/{WhatsNewDialog-C-hpR8y4.js → WhatsNewDialog-9upxbs9b.js} +1 -1
  54. package/web/dist/assets/{WorkerModal-EuNxsCtr.js → WorkerModal-DAC0fORb.js} +1 -1
  55. package/web/dist/assets/{WorkflowsDrawer-CoT6kWcM.js → WorkflowsDrawer-Dqv4vNZW.js} +1 -1
  56. package/web/dist/assets/{WorkspaceMemoryDrawer-BXx4Z0kx.js → WorkspaceMemoryDrawer-QvVUM6qp.js} +1 -1
  57. package/web/dist/assets/{WorkspaceTaskDrawer-DKRRttxQ.js → WorkspaceTaskDrawer-CdiraIBo.js} +1 -1
  58. package/web/dist/assets/index-B1vvo0-M.js +84 -0
  59. package/web/dist/assets/{search-KbSLfFB8.js → search-DgMNPsfM.js} +1 -1
  60. package/web/dist/assets/{square-terminal-C89RIZhu.js → square-terminal-CiDDFh5Z.js} +1 -1
  61. package/web/dist/index.html +1 -1
  62. package/web/dist/sw.js +1 -1
  63. package/web/dist/assets/index-D43vHIy2.js +0 -84
@@ -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
- getLiveRun: (runId) => services.shellRuntime.getLiveRun(runId) ?? services.agentRuntime.getLiveRun(runId),
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,
@@ -3,11 +3,14 @@ import type { WorkerRole } from '../shared/types.js';
3
3
  import type { UiLanguage } from '../shared/ui-language.js';
4
4
  export { getScenarioPreset, getScenarioWorkerDescription, SCENARIO_PRESETS, type ScenarioId, type ScenarioPreset, type ScenarioWorkerSpec, } from '../shared/scenario-presets.js';
5
5
  /**
6
- * `<stem>-<4 base36 chars>` short, `team send`-safe (no spaces, lowercase),
7
- * and unlikely to collide. The caller passes an `isTaken` probe against the
8
- * live roster; on the (rare) collision we just roll again.
6
+ * Scenario member names come from the same role-flavored historical-figure
7
+ * pool the Add Member dialog uses. When a pool is exhausted in a busy
8
+ * workspace, append a short suffix instead of failing the whole scenario.
9
9
  */
10
- export declare const buildScenarioWorkerName: (spec: Pick<ScenarioWorkerSpec, "nameStem">, isTaken: (name: string) => boolean, maxAttempts?: number) => string;
10
+ export declare const buildScenarioWorkerName: (spec: Pick<ScenarioWorkerSpec, "nameStem" | "role">, usedNames: ReadonlySet<string>, input?: {
11
+ language?: UiLanguage;
12
+ nextUint32?: () => number;
13
+ }, maxAttempts?: number) => string;
11
14
  export interface ScenarioKickoffInput {
12
15
  scenarioId: string;
13
16
  goal: string;
@@ -1,15 +1,25 @@
1
1
  import { randomBytes } from 'node:crypto';
2
+ import { generateWorkerName } from '../shared/random-worker-name.js';
2
3
  export { getScenarioPreset, getScenarioWorkerDescription, SCENARIO_PRESETS, } from '../shared/scenario-presets.js';
3
4
  /**
4
- * `<stem>-<4 base36 chars>` short, `team send`-safe (no spaces, lowercase),
5
- * and unlikely to collide. The caller passes an `isTaken` probe against the
6
- * live roster; on the (rare) collision we just roll again.
5
+ * Scenario member names come from the same role-flavored historical-figure
6
+ * pool the Add Member dialog uses. When a pool is exhausted in a busy
7
+ * workspace, append a short suffix instead of failing the whole scenario.
7
8
  */
8
- export const buildScenarioWorkerName = (spec, isTaken, maxAttempts = 16) => {
9
+ export const buildScenarioWorkerName = (spec, usedNames, input = {}, maxAttempts = 16) => {
10
+ const candidate = generateWorkerName({
11
+ role: spec.role,
12
+ usedNames,
13
+ ...(input.language !== undefined ? { language: input.language } : {}),
14
+ ...(input.nextUint32 !== undefined ? { nextUint32: input.nextUint32 } : {}),
15
+ });
16
+ if (!usedNames.has(candidate))
17
+ return candidate;
18
+ const base = candidate || spec.nameStem;
9
19
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
10
20
  const suffix = randomBytes(3).readUIntBE(0, 3).toString(36).padStart(4, '0').slice(-4);
11
- const name = `${spec.nameStem}-${suffix}`;
12
- if (!isTaken(name))
21
+ const name = `${base}-${suffix}`;
22
+ if (!usedNames.has(name))
13
23
  return name;
14
24
  }
15
25
  throw new Error(`Could not generate a unique member name for: ${spec.nameStem}`);
@@ -0,0 +1,2 @@
1
+ import type { Database } from 'better-sqlite3';
2
+ export declare const applySchemaVersion37: (db: Database) => void;
@@ -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
+ };
@@ -1,3 +1,3 @@
1
1
  import type { Database } from 'better-sqlite3';
2
- export declare const CURRENT_SCHEMA_VERSION = 36;
2
+ export declare const CURRENT_SCHEMA_VERSION = 37;
3
3
  export declare const initializeRuntimeDatabase: (db: Database) => void;
@@ -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
- export const CURRENT_SCHEMA_VERSION = 36;
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
@@ -15,6 +15,7 @@ const ORCHESTRATOR_COMMANDS = new Set([
15
15
  'spawn',
16
16
  'dismiss',
17
17
  'workflow',
18
+ 'goal_report',
18
19
  ]);
19
20
  const WORKER_COMMANDS = new Set([
20
21
  'report',
@@ -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
  }
@@ -2,6 +2,7 @@ import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
2
2
  import { HIVE_REMOTE_SECRET_HEADER } from './remote-loopback-auth.js';
3
3
  export const createUiAuth = () => {
4
4
  const token = randomUUID();
5
+ const supervisorToken = randomUUID();
5
6
  // 32 bytes of CSPRNG entropy, fresh every boot. base64url so it travels as a
6
7
  // clean header value. Held only in this closure.
7
8
  const tunnelSecret = randomBytes(32).toString('base64url');
@@ -10,9 +11,15 @@ export const createUiAuth = () => {
10
11
  getToken() {
11
12
  return token;
12
13
  },
14
+ getSupervisorToken() {
15
+ return supervisorToken;
16
+ },
13
17
  validate(input) {
14
18
  return input === token;
15
19
  },
20
+ validateSupervisorToken(input) {
21
+ return input === supervisorToken;
22
+ },
16
23
  isTunnelRequest(request) {
17
24
  const raw = request.headers[HIVE_REMOTE_SECRET_HEADER];
18
25
  const got = Array.isArray(raw) ? raw[0] : raw;
@@ -0,0 +1,9 @@
1
+ import type { WorkerRole } from './types.js';
2
+ import type { UiLanguage } from './ui-language.js';
3
+ export interface GenerateWorkerNameOptions {
4
+ language?: UiLanguage;
5
+ role?: WorkerRole;
6
+ usedNames?: ReadonlySet<string>;
7
+ nextUint32?: () => number;
8
+ }
9
+ export declare const generateWorkerName: ({ language, role, usedNames, nextUint32, }?: GenerateWorkerNameOptions) => string;