@tt-a1i/hive 1.7.0 → 2.0.2
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 +60 -0
- package/README.en.md +73 -11
- package/README.md +41 -8
- package/dist/src/cli/hive-remote.d.ts +46 -0
- package/dist/src/cli/hive-remote.js +257 -0
- package/dist/src/cli/hive-update.js +7 -2
- package/dist/src/cli/hive.d.ts +6 -0
- package/dist/src/cli/hive.js +64 -0
- package/dist/src/cli/team.d.ts +22 -0
- package/dist/src/cli/team.js +255 -5
- package/dist/src/server/agent-command-resolver.js +10 -3
- package/dist/src/server/agent-exit-classification.d.ts +6 -0
- package/dist/src/server/agent-exit-classification.js +6 -0
- package/dist/src/server/agent-manager-support.d.ts +2 -1
- package/dist/src/server/agent-manager-support.js +59 -15
- package/dist/src/server/agent-manager.d.ts +3 -0
- package/dist/src/server/agent-manager.js +22 -7
- package/dist/src/server/agent-run-bootstrap.d.ts +14 -0
- package/dist/src/server/agent-run-bootstrap.js +11 -4
- package/dist/src/server/agent-run-exit-handler.js +14 -8
- package/dist/src/server/agent-run-starter.d.ts +3 -1
- package/dist/src/server/agent-run-starter.js +22 -5
- package/dist/src/server/agent-run-sync.js +13 -5
- package/dist/src/server/agent-runtime-types.d.ts +1 -0
- package/dist/src/server/agent-runtime.d.ts +2 -1
- package/dist/src/server/agent-runtime.js +9 -2
- package/dist/src/server/agent-startup-instructions.d.ts +2 -1
- package/dist/src/server/agent-startup-instructions.js +8 -4
- package/dist/src/server/agent-stdin-dispatcher.d.ts +4 -2
- package/dist/src/server/agent-stdin-dispatcher.js +35 -3
- package/dist/src/server/command-preset-defaults.d.ts +6 -1
- package/dist/src/server/command-preset-defaults.js +56 -0
- package/dist/src/server/fs-browse.d.ts +2 -0
- package/dist/src/server/fs-browse.js +165 -31
- package/dist/src/server/fs-pick-folder.js +6 -69
- package/dist/src/server/fs-sandbox.d.ts +5 -3
- package/dist/src/server/fs-sandbox.js +5 -3
- package/dist/src/server/hive-team-guidance.js +18 -6
- package/dist/src/server/machine-name.d.ts +2 -0
- package/dist/src/server/machine-name.js +13 -0
- package/dist/src/server/open-target-commands.d.ts +1 -0
- package/dist/src/server/open-target-commands.js +4 -1
- package/dist/src/server/orchestrator-autostart.js +1 -1
- package/dist/src/server/platform-path.d.ts +1 -0
- package/dist/src/server/platform-path.js +14 -1
- package/dist/src/server/post-start-input-writer.js +50 -13
- package/dist/src/server/preset-launch-support.js +1 -0
- package/dist/src/server/recovery-summary.d.ts +2 -1
- package/dist/src/server/recovery-summary.js +2 -1
- package/dist/src/server/remote-audit-store.d.ts +51 -0
- package/dist/src/server/remote-audit-store.js +108 -0
- package/dist/src/server/remote-config-keys.d.ts +17 -0
- package/dist/src/server/remote-config-keys.js +27 -0
- package/dist/src/server/remote-control-constants.d.ts +30 -0
- package/dist/src/server/remote-control-constants.js +29 -0
- package/dist/src/server/remote-device-session.d.ts +40 -0
- package/dist/src/server/remote-device-session.js +22 -0
- package/dist/src/server/remote-device-store.d.ts +36 -0
- package/dist/src/server/remote-device-store.js +67 -0
- package/dist/src/server/remote-frame-bridge.d.ts +102 -0
- package/dist/src/server/remote-frame-bridge.js +791 -0
- package/dist/src/server/remote-gateway-client.d.ts +14 -0
- package/dist/src/server/remote-gateway-client.js +36 -0
- package/dist/src/server/remote-loopback-auth.d.ts +6 -0
- package/dist/src/server/remote-loopback-auth.js +112 -0
- package/dist/src/server/remote-pairing-tunnel.d.ts +59 -0
- package/dist/src/server/remote-pairing-tunnel.js +146 -0
- package/dist/src/server/remote-pairing.d.ts +58 -0
- package/dist/src/server/remote-pairing.js +237 -0
- package/dist/src/server/remote-tunnel.d.ts +113 -0
- package/dist/src/server/remote-tunnel.js +514 -0
- package/dist/src/server/restart-policy-support.d.ts +4 -1
- package/dist/src/server/restart-policy-support.js +3 -1
- package/dist/src/server/restart-policy.d.ts +1 -1
- package/dist/src/server/restart-policy.js +19 -3
- package/dist/src/server/route-types.d.ts +1 -1
- package/dist/src/server/routes-dispatches.js +1 -1
- package/dist/src/server/routes-fs.js +3 -3
- package/dist/src/server/routes-marketplace.js +2 -2
- package/dist/src/server/routes-open-workspace.js +1 -1
- package/dist/src/server/routes-remote.d.ts +2 -0
- package/dist/src/server/routes-remote.js +166 -0
- package/dist/src/server/routes-runtime.js +6 -6
- package/dist/src/server/routes-settings.js +16 -16
- package/dist/src/server/routes-tasks.js +2 -2
- package/dist/src/server/routes-team-memory.d.ts +2 -0
- package/dist/src/server/routes-team-memory.js +154 -0
- package/dist/src/server/routes-team-recall.d.ts +2 -0
- package/dist/src/server/routes-team-recall.js +119 -0
- package/dist/src/server/routes-team.js +31 -9
- package/dist/src/server/routes-ui.js +11 -1
- package/dist/src/server/routes-workflow-schedules.js +3 -3
- package/dist/src/server/routes-workflows.js +5 -5
- package/dist/src/server/routes-workspace-memory-dreams.d.ts +2 -0
- package/dist/src/server/routes-workspace-memory-dreams.js +105 -0
- package/dist/src/server/routes-workspace-memory.d.ts +2 -0
- package/dist/src/server/routes-workspace-memory.js +215 -0
- package/dist/src/server/routes-workspaces.js +9 -9
- package/dist/src/server/routes.js +10 -0
- package/dist/src/server/runtime-database.d.ts +1 -0
- package/dist/src/server/runtime-database.js +27 -2
- package/dist/src/server/runtime-restart-policy.d.ts +3 -1
- package/dist/src/server/runtime-restart-policy.js +2 -1
- package/dist/src/server/runtime-store-contract.d.ts +37 -0
- package/dist/src/server/runtime-store-dream.d.ts +23 -0
- package/dist/src/server/runtime-store-dream.js +16 -0
- package/dist/src/server/runtime-store-helpers.d.ts +20 -0
- package/dist/src/server/runtime-store-helpers.js +81 -7
- package/dist/src/server/runtime-store-memory.d.ts +33 -0
- package/dist/src/server/runtime-store-memory.js +37 -0
- package/dist/src/server/runtime-store-remote.d.ts +5 -0
- package/dist/src/server/runtime-store-remote.js +45 -0
- package/dist/src/server/runtime-store-workflows.js +2 -0
- package/dist/src/server/runtime-store.js +14 -3
- package/dist/src/server/session-capture-claude.d.ts +1 -1
- package/dist/src/server/session-capture-claude.js +7 -4
- package/dist/src/server/session-capture-codex.js +4 -5
- package/dist/src/server/session-capture-gemini.js +4 -5
- package/dist/src/server/session-capture-opencode.d.ts +4 -4
- package/dist/src/server/session-capture-opencode.js +20 -12
- package/dist/src/server/session-capture-qwen.d.ts +5 -0
- package/dist/src/server/session-capture-qwen.js +104 -0
- package/dist/src/server/session-capture.d.ts +17 -0
- package/dist/src/server/session-capture.js +16 -0
- package/dist/src/server/sqlite-schema-v23.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v23.js +43 -0
- package/dist/src/server/sqlite-schema-v24.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v24.js +34 -0
- package/dist/src/server/sqlite-schema-v25.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v25.js +127 -0
- package/dist/src/server/sqlite-schema-v26.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v26.js +56 -0
- package/dist/src/server/sqlite-schema-v27.d.ts +6 -0
- package/dist/src/server/sqlite-schema-v27.js +92 -0
- package/dist/src/server/sqlite-schema-v28.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v28.js +19 -0
- package/dist/src/server/sqlite-schema-v29.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v29.js +27 -0
- package/dist/src/server/sqlite-schema-v30.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v30.js +27 -0
- package/dist/src/server/sqlite-schema-v31.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v31.js +30 -0
- package/dist/src/server/sqlite-schema.d.ts +1 -1
- package/dist/src/server/sqlite-schema.js +49 -1
- package/dist/src/server/startup-command-parser.js +5 -1
- package/dist/src/server/tasks-file-watcher.d.ts +2 -0
- package/dist/src/server/tasks-file-watcher.js +15 -6
- package/dist/src/server/tasks-file.js +30 -5
- package/dist/src/server/tasks-websocket-server.js +4 -0
- package/dist/src/server/team-authz.d.ts +1 -1
- package/dist/src/server/team-authz.js +13 -1
- package/dist/src/server/team-list-enrichment.js +3 -1
- package/dist/src/server/team-memory-digest.d.ts +52 -0
- package/dist/src/server/team-memory-digest.js +200 -0
- package/dist/src/server/team-memory-dream-applier.d.ts +5 -0
- package/dist/src/server/team-memory-dream-applier.js +234 -0
- package/dist/src/server/team-memory-dream-http-serializers.d.ts +13 -0
- package/dist/src/server/team-memory-dream-http-serializers.js +12 -0
- package/dist/src/server/team-memory-dream-ops.d.ts +40 -0
- package/dist/src/server/team-memory-dream-ops.js +153 -0
- package/dist/src/server/team-memory-dream-reverter.d.ts +22 -0
- package/dist/src/server/team-memory-dream-reverter.js +221 -0
- package/dist/src/server/team-memory-dream-run-store.d.ts +23 -0
- package/dist/src/server/team-memory-dream-run-store.js +211 -0
- package/dist/src/server/team-memory-dream-runner.d.ts +37 -0
- package/dist/src/server/team-memory-dream-runner.js +178 -0
- package/dist/src/server/team-memory-dream-scheduler.d.ts +32 -0
- package/dist/src/server/team-memory-dream-scheduler.js +115 -0
- package/dist/src/server/team-memory-dream-store.d.ts +19 -0
- package/dist/src/server/team-memory-dream-store.js +16 -0
- package/dist/src/server/team-memory-dream-types.d.ts +104 -0
- package/dist/src/server/team-memory-dream-types.js +23 -0
- package/dist/src/server/team-memory-export.d.ts +22 -0
- package/dist/src/server/team-memory-export.js +220 -0
- package/dist/src/server/team-memory-feature.d.ts +12 -0
- package/dist/src/server/team-memory-feature.js +12 -0
- package/dist/src/server/team-memory-http-serializers.d.ts +102 -0
- package/dist/src/server/team-memory-http-serializers.js +46 -0
- package/dist/src/server/team-memory-injection.d.ts +31 -0
- package/dist/src/server/team-memory-injection.js +49 -0
- package/dist/src/server/team-memory-store.d.ts +116 -0
- package/dist/src/server/team-memory-store.js +513 -0
- package/dist/src/server/team-operations.d.ts +5 -1
- package/dist/src/server/team-operations.js +46 -16
- package/dist/src/server/team-recall-store.d.ts +38 -0
- package/dist/src/server/team-recall-store.js +205 -0
- package/dist/src/server/terminal-input-profile.d.ts +1 -1
- package/dist/src/server/terminal-input-profile.js +18 -0
- package/dist/src/server/terminal-ws-server.js +6 -0
- package/dist/src/server/ui-auth-helpers.d.ts +1 -1
- package/dist/src/server/ui-auth-helpers.js +7 -1
- package/dist/src/server/ui-auth.d.ts +3 -0
- package/dist/src/server/ui-auth.js +21 -1
- package/dist/src/server/workflow-cli-policy.d.ts +2 -3
- package/dist/src/server/workflow-cli-policy.js +3 -3
- package/dist/src/server/workflow-runner.d.ts +1 -0
- package/dist/src/server/workflow-runner.js +9 -4
- package/dist/src/server/workspace-path-validation.js +6 -2
- package/dist/src/server/workspace-store.d.ts +1 -1
- package/dist/src/server/workspace-store.js +35 -9
- package/dist/src/shared/fs-browse.d.ts +1 -0
- package/dist/src/shared/fs-browse.js +1 -0
- package/dist/src/shared/path-input.d.ts +12 -0
- package/dist/src/shared/path-input.js +22 -0
- package/dist/src/shared/remote-bridge-routing.d.ts +19 -0
- package/dist/src/shared/remote-bridge-routing.js +141 -0
- package/dist/src/shared/remote-crypto.d.ts +138 -0
- package/dist/src/shared/remote-crypto.js +427 -0
- package/dist/src/shared/remote-pairing-code.d.ts +7 -0
- package/dist/src/shared/remote-pairing-code.js +47 -0
- package/dist/src/shared/remote-protocol.d.ts +160 -0
- package/dist/src/shared/remote-protocol.js +526 -0
- package/dist/src/shared/team-memory.d.ts +11 -0
- package/dist/src/shared/team-memory.js +10 -0
- package/dist/src/shared/team-recall.d.ts +1 -0
- package/dist/src/shared/team-recall.js +1 -0
- package/dist/src/shared/types.d.ts +4 -5
- package/package.json +12 -5
- package/scripts/postinstall-native-artifacts.mjs +113 -0
- package/web/dist/assets/AddWorkerDialog-CbV75qUX.js +2 -0
- package/web/dist/assets/AddWorkspaceFlow-CwV-7wPx.js +1 -0
- package/web/dist/assets/FirstRunWizard-a6PWIK3x.js +1 -0
- package/web/dist/assets/MarketplaceDrawer-Dd8WIA8T.js +67 -0
- package/web/dist/assets/TaskGraphDrawer-Bk5WFIk_.js +1 -0
- package/web/dist/assets/{WhatsNewDialog-CHkZeINH.js → WhatsNewDialog-C2VZaip0.js} +1 -1
- package/web/dist/assets/WorkerModal-DucW-9YT.js +1 -0
- package/web/dist/assets/WorkflowsDrawer-Bjf4olbR.js +1 -0
- package/web/dist/assets/WorkspaceMemoryDrawer-DglCy_5f.js +1 -0
- package/web/dist/assets/WorkspaceTaskDrawer-BIWwISvA.js +1 -0
- package/web/dist/assets/index-BAiLYajK.css +1 -0
- package/web/dist/assets/index-BV2k9Dts.js +73 -0
- package/web/dist/assets/search-Bk2HQvO7.js +1 -0
- package/web/dist/assets/square-terminal-D93m9hfY.js +1 -0
- package/web/dist/cli-icons/agy.png +0 -0
- package/web/dist/cli-icons/cursor.ico +0 -0
- package/web/dist/cli-icons/grok.ico +0 -0
- package/web/dist/cli-icons/qwen.png +0 -0
- package/web/dist/index.html +8 -3
- package/web/dist/sw.js +1 -1
- package/scripts/fix-runtime-artifacts.mjs +0 -33
- package/web/dist/assets/AddWorkerDialog-BRUxpa3f.js +0 -2
- package/web/dist/assets/AddWorkspaceDialog-D56x5JCb.js +0 -1
- package/web/dist/assets/FirstRunWizard-BFVaMIsE.js +0 -1
- package/web/dist/assets/MarketplaceDrawer-DeEZ35dN.js +0 -76
- package/web/dist/assets/WorkerModal-BBCuMLIa.js +0 -1
- package/web/dist/assets/WorkspaceTaskDrawer-CpZHAcj1.js +0 -1
- package/web/dist/assets/WorkspaceTerminalPanels-7If2mDyp.js +0 -1
- package/web/dist/assets/WorkspaceTerminalPanels-DDGTF8rc.css +0 -1
- package/web/dist/assets/index-5zh61jMg.css +0 -1
- package/web/dist/assets/index-CxNL0O-C.js +0 -73
- package/web/dist/assets/path-join-7MR1s7b1.js +0 -1
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { MEMORY_QUERY_MAX_CHARS, MEMORY_SEARCH_DEFAULT_LIMIT, MEMORY_SEARCH_MAX_LIMIT, } from '../shared/team-memory.js';
|
|
3
|
+
export class MemoryEntryNotFoundError extends Error {
|
|
4
|
+
memoryId;
|
|
5
|
+
workspaceId;
|
|
6
|
+
constructor(memoryId, workspaceId) {
|
|
7
|
+
super(`Memory entry not found in workspace: ${memoryId}`);
|
|
8
|
+
this.name = 'MemoryEntryNotFoundError';
|
|
9
|
+
this.memoryId = memoryId;
|
|
10
|
+
this.workspaceId = workspaceId;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class MemoryEntryStatusError extends Error {
|
|
14
|
+
actualStatus;
|
|
15
|
+
memoryId;
|
|
16
|
+
expectedStatus;
|
|
17
|
+
constructor(memoryId, expectedStatus, actualStatus) {
|
|
18
|
+
const expected = Array.isArray(expectedStatus) ? expectedStatus.join(' or ') : expectedStatus;
|
|
19
|
+
super(`Memory entry ${memoryId} expected status ${expected}, got ${actualStatus}`);
|
|
20
|
+
this.name = 'MemoryEntryStatusError';
|
|
21
|
+
this.memoryId = memoryId;
|
|
22
|
+
this.expectedStatus = expectedStatus;
|
|
23
|
+
this.actualStatus = actualStatus;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const hashText = (text) => createHash('sha256').update(text).digest('hex');
|
|
27
|
+
const excerptFor = (text) => [...text].slice(0, 500).join('');
|
|
28
|
+
const clampLimit = (value) => {
|
|
29
|
+
if (value === undefined || !Number.isInteger(value) || value < 0) {
|
|
30
|
+
return MEMORY_SEARCH_DEFAULT_LIMIT;
|
|
31
|
+
}
|
|
32
|
+
return Math.min(value, MEMORY_SEARCH_MAX_LIMIT);
|
|
33
|
+
};
|
|
34
|
+
const clampDigestLimit = (value) => {
|
|
35
|
+
if (value === undefined || !Number.isInteger(value) || value <= 0)
|
|
36
|
+
return 20;
|
|
37
|
+
return Math.min(value, 50);
|
|
38
|
+
};
|
|
39
|
+
const clampListLimit = (value) => {
|
|
40
|
+
if (value === undefined || !Number.isInteger(value) || value <= 0)
|
|
41
|
+
return MEMORY_SEARCH_MAX_LIMIT;
|
|
42
|
+
return Math.min(value, MEMORY_SEARCH_MAX_LIMIT);
|
|
43
|
+
};
|
|
44
|
+
const quoteFtsToken = (value) => `"${value.replaceAll('"', '""')}"`;
|
|
45
|
+
const toSearchTerms = (query) => query.trim().split(/\s+/).filter(Boolean);
|
|
46
|
+
const toFtsQuery = (query) => toSearchTerms(query).map(quoteFtsToken).join(' AND ');
|
|
47
|
+
const hasShortTerm = (terms) => terms.some((term) => [...term].length < 3);
|
|
48
|
+
const escapeLike = (value) => `%${value.replaceAll('\\', '\\\\').replaceAll('%', '\\%').replaceAll('_', '\\_')}%`;
|
|
49
|
+
const parseTags = (value) => {
|
|
50
|
+
if (!value)
|
|
51
|
+
return [];
|
|
52
|
+
const parsed = JSON.parse(value);
|
|
53
|
+
return Array.isArray(parsed) ? parsed.filter((tag) => typeof tag === 'string') : [];
|
|
54
|
+
};
|
|
55
|
+
const toEntryRecord = (row) => ({
|
|
56
|
+
archivedAt: row.archived_at,
|
|
57
|
+
body: row.body,
|
|
58
|
+
confidence: row.confidence,
|
|
59
|
+
createdAt: row.created_at,
|
|
60
|
+
disabled: row.disabled === 1,
|
|
61
|
+
id: row.id,
|
|
62
|
+
kind: row.kind,
|
|
63
|
+
lastInjectedAt: row.last_injected_at,
|
|
64
|
+
pinned: row.pinned === 1,
|
|
65
|
+
scope: row.scope,
|
|
66
|
+
source: row.source,
|
|
67
|
+
status: row.status,
|
|
68
|
+
tags: parseTags(row.tags),
|
|
69
|
+
updatedAt: row.updated_at,
|
|
70
|
+
workspaceId: row.workspace_id,
|
|
71
|
+
});
|
|
72
|
+
const toSourceRecord = (row) => ({
|
|
73
|
+
actorAgentIdSnapshot: row.actor_agent_id_snapshot,
|
|
74
|
+
actorNameSnapshot: row.actor_name_snapshot,
|
|
75
|
+
actorRoleSnapshot: row.actor_role_snapshot,
|
|
76
|
+
createdAt: row.created_at,
|
|
77
|
+
excerpt: row.excerpt,
|
|
78
|
+
id: row.id,
|
|
79
|
+
memoryId: row.memory_id,
|
|
80
|
+
sourceId: row.source_id,
|
|
81
|
+
sourceSequence: row.source_sequence,
|
|
82
|
+
sourceType: row.source_type,
|
|
83
|
+
textHash: row.text_hash,
|
|
84
|
+
});
|
|
85
|
+
export const createTeamMemoryStore = (db) => {
|
|
86
|
+
const nextFtsRowid = () => db.prepare('SELECT COALESCE(MAX(fts_rowid), 0) + 1 AS next FROM memory_entries').get().next;
|
|
87
|
+
const listSources = (memoryId) => db
|
|
88
|
+
.prepare(`SELECT *
|
|
89
|
+
FROM memory_sources
|
|
90
|
+
WHERE memory_id = ?
|
|
91
|
+
ORDER BY created_at ASC, id ASC`)
|
|
92
|
+
.all(memoryId).map(toSourceRecord);
|
|
93
|
+
const toEntryWithSources = (row) => ({
|
|
94
|
+
...toEntryRecord(row),
|
|
95
|
+
sources: listSources(row.id),
|
|
96
|
+
});
|
|
97
|
+
const getEntryWithSources = (workspaceId, memoryId) => {
|
|
98
|
+
const row = db
|
|
99
|
+
.prepare(`SELECT *
|
|
100
|
+
FROM memory_entries
|
|
101
|
+
WHERE id = ?
|
|
102
|
+
AND scope = 'workspace'
|
|
103
|
+
AND workspace_id = ?
|
|
104
|
+
LIMIT 1`)
|
|
105
|
+
.get(memoryId, workspaceId);
|
|
106
|
+
if (!row)
|
|
107
|
+
return undefined;
|
|
108
|
+
return toEntryWithSources(row);
|
|
109
|
+
};
|
|
110
|
+
const requireEntryWithSources = (workspaceId, memoryId) => {
|
|
111
|
+
const entry = getEntryWithSources(workspaceId, memoryId);
|
|
112
|
+
if (!entry)
|
|
113
|
+
throw new MemoryEntryNotFoundError(memoryId, workspaceId);
|
|
114
|
+
return entry;
|
|
115
|
+
};
|
|
116
|
+
const searchFts = (table, workspaceId, ftsQuery, statuses, includeDisabled) => {
|
|
117
|
+
if (statuses.length === 0)
|
|
118
|
+
return [];
|
|
119
|
+
const indexName = table === 'memory_fts' ? 'unicode' : 'trigram';
|
|
120
|
+
const statusPlaceholders = statuses.map(() => '?').join(', ');
|
|
121
|
+
return db
|
|
122
|
+
.prepare(`SELECT
|
|
123
|
+
e.*,
|
|
124
|
+
? AS index_name,
|
|
125
|
+
bm25(${table}) AS score
|
|
126
|
+
FROM ${table}
|
|
127
|
+
JOIN memory_entries e ON e.fts_rowid = ${table}.rowid
|
|
128
|
+
WHERE ${table} MATCH ?
|
|
129
|
+
AND e.scope = 'workspace'
|
|
130
|
+
AND e.workspace_id = ?
|
|
131
|
+
${includeDisabled ? '' : 'AND e.disabled = 0'}
|
|
132
|
+
AND e.status IN (${statusPlaceholders})`)
|
|
133
|
+
.all(indexName, ftsQuery, workspaceId, ...statuses);
|
|
134
|
+
};
|
|
135
|
+
const searchLike = (workspaceId, terms, statuses, includeDisabled) => {
|
|
136
|
+
if (terms.length === 0 || statuses.length === 0)
|
|
137
|
+
return [];
|
|
138
|
+
const predicates = terms
|
|
139
|
+
.map(() => "(e.body LIKE ? ESCAPE '\\' OR COALESCE(e.tags, '') LIKE ? ESCAPE '\\')")
|
|
140
|
+
.join(' AND ');
|
|
141
|
+
const statusPlaceholders = statuses.map(() => '?').join(', ');
|
|
142
|
+
return db
|
|
143
|
+
.prepare(`SELECT
|
|
144
|
+
e.*,
|
|
145
|
+
'like' AS index_name,
|
|
146
|
+
0 AS score
|
|
147
|
+
FROM memory_entries e
|
|
148
|
+
WHERE e.scope = 'workspace'
|
|
149
|
+
AND e.workspace_id = ?
|
|
150
|
+
${includeDisabled ? '' : 'AND e.disabled = 0'}
|
|
151
|
+
AND e.status IN (${statusPlaceholders})
|
|
152
|
+
AND ${predicates}`)
|
|
153
|
+
.all(workspaceId, ...statuses, ...terms.flatMap((term) => [escapeLike(term), escapeLike(term)]));
|
|
154
|
+
};
|
|
155
|
+
const searchEntries = (workspaceId, query, options = {}) => {
|
|
156
|
+
if ([...query].length > MEMORY_QUERY_MAX_CHARS)
|
|
157
|
+
return [];
|
|
158
|
+
const ftsQuery = toFtsQuery(query);
|
|
159
|
+
if (!ftsQuery)
|
|
160
|
+
return [];
|
|
161
|
+
const terms = toSearchTerms(query);
|
|
162
|
+
const statuses = options.statuses ?? ['active'];
|
|
163
|
+
const includeDisabled = options.includeDisabled ?? false;
|
|
164
|
+
const rows = [
|
|
165
|
+
...searchFts('memory_fts', workspaceId, ftsQuery, statuses, includeDisabled),
|
|
166
|
+
...searchFts('memory_fts_trigram', workspaceId, ftsQuery, statuses, includeDisabled),
|
|
167
|
+
...(hasShortTerm(terms) ? searchLike(workspaceId, terms, statuses, includeDisabled) : []),
|
|
168
|
+
];
|
|
169
|
+
const byId = new Map();
|
|
170
|
+
for (const row of rows) {
|
|
171
|
+
const previous = byId.get(row.id);
|
|
172
|
+
if (!previous || row.score < previous.score)
|
|
173
|
+
byId.set(row.id, row);
|
|
174
|
+
}
|
|
175
|
+
return [...byId.values()]
|
|
176
|
+
.sort((a, b) => a.score - b.score || b.updated_at - a.updated_at)
|
|
177
|
+
.slice(0, clampLimit(options.limit))
|
|
178
|
+
.map((row) => ({
|
|
179
|
+
...toEntryWithSources(row),
|
|
180
|
+
indexName: row.index_name,
|
|
181
|
+
score: row.score,
|
|
182
|
+
}));
|
|
183
|
+
};
|
|
184
|
+
const listEntries = (workspaceId, options = {}) => {
|
|
185
|
+
const statuses = options.statuses ?? ['active'];
|
|
186
|
+
if (statuses.length === 0)
|
|
187
|
+
return [];
|
|
188
|
+
const statusPlaceholders = statuses.map(() => '?').join(', ');
|
|
189
|
+
return db
|
|
190
|
+
.prepare(`SELECT *
|
|
191
|
+
FROM memory_entries
|
|
192
|
+
WHERE scope = 'workspace'
|
|
193
|
+
AND workspace_id = ?
|
|
194
|
+
AND status IN (${statusPlaceholders})
|
|
195
|
+
ORDER BY
|
|
196
|
+
CASE status
|
|
197
|
+
WHEN 'candidate' THEN 0
|
|
198
|
+
WHEN 'active' THEN 1
|
|
199
|
+
WHEN 'archived' THEN 2
|
|
200
|
+
ELSE 3
|
|
201
|
+
END ASC,
|
|
202
|
+
pinned DESC,
|
|
203
|
+
updated_at DESC,
|
|
204
|
+
created_at DESC,
|
|
205
|
+
id ASC
|
|
206
|
+
LIMIT ?`)
|
|
207
|
+
.all(workspaceId, ...statuses, clampListLimit(options.limit)).map(toEntryWithSources);
|
|
208
|
+
};
|
|
209
|
+
const listAllEntries = (workspaceId) => db
|
|
210
|
+
.prepare(`SELECT *
|
|
211
|
+
FROM memory_entries
|
|
212
|
+
WHERE scope = 'workspace'
|
|
213
|
+
AND workspace_id = ?
|
|
214
|
+
ORDER BY
|
|
215
|
+
CASE status
|
|
216
|
+
WHEN 'candidate' THEN 0
|
|
217
|
+
WHEN 'active' THEN 1
|
|
218
|
+
WHEN 'archived' THEN 2
|
|
219
|
+
ELSE 3
|
|
220
|
+
END ASC,
|
|
221
|
+
pinned DESC,
|
|
222
|
+
updated_at DESC,
|
|
223
|
+
created_at DESC,
|
|
224
|
+
id ASC`)
|
|
225
|
+
.all(workspaceId).map(toEntryWithSources);
|
|
226
|
+
const listDigestEntries = (workspaceId, options = {}) => db
|
|
227
|
+
.prepare(`SELECT *
|
|
228
|
+
FROM memory_entries
|
|
229
|
+
WHERE scope = 'workspace'
|
|
230
|
+
AND workspace_id = ?
|
|
231
|
+
AND status = 'active'
|
|
232
|
+
AND disabled = 0
|
|
233
|
+
ORDER BY
|
|
234
|
+
pinned DESC,
|
|
235
|
+
CASE source WHEN 'manual' THEN 0 ELSE 1 END ASC,
|
|
236
|
+
COALESCE(confidence, 0) DESC,
|
|
237
|
+
updated_at DESC,
|
|
238
|
+
created_at DESC,
|
|
239
|
+
id ASC
|
|
240
|
+
LIMIT ?`)
|
|
241
|
+
.all(workspaceId, clampDigestLimit(options.limit)).map(toEntryWithSources);
|
|
242
|
+
const listExportEntries = (workspaceId) => db
|
|
243
|
+
.prepare(`SELECT *
|
|
244
|
+
FROM memory_entries
|
|
245
|
+
WHERE scope = 'workspace'
|
|
246
|
+
AND workspace_id = ?
|
|
247
|
+
AND status = 'active'
|
|
248
|
+
ORDER BY
|
|
249
|
+
kind ASC,
|
|
250
|
+
pinned DESC,
|
|
251
|
+
CASE source WHEN 'manual' THEN 0 ELSE 1 END ASC,
|
|
252
|
+
COALESCE(confidence, 0) DESC,
|
|
253
|
+
updated_at DESC,
|
|
254
|
+
created_at DESC,
|
|
255
|
+
id ASC`)
|
|
256
|
+
.all(workspaceId).map(toEntryWithSources);
|
|
257
|
+
const addEntry = (input) => {
|
|
258
|
+
const id = randomUUID();
|
|
259
|
+
const sourceId = randomUUID();
|
|
260
|
+
const now = Date.now();
|
|
261
|
+
const status = input.actor.role === 'orchestrator' ? 'active' : 'candidate';
|
|
262
|
+
const source = input.source ?? 'manual';
|
|
263
|
+
const confidence = input.confidence ?? (source === 'manual' ? 1 : null);
|
|
264
|
+
const tags = JSON.stringify(input.tags ?? []);
|
|
265
|
+
db.transaction(() => {
|
|
266
|
+
const ftsRowid = nextFtsRowid();
|
|
267
|
+
db.prepare(`INSERT INTO memory_entries (
|
|
268
|
+
id,
|
|
269
|
+
workspace_id,
|
|
270
|
+
scope,
|
|
271
|
+
fts_rowid,
|
|
272
|
+
kind,
|
|
273
|
+
body,
|
|
274
|
+
tags,
|
|
275
|
+
status,
|
|
276
|
+
source,
|
|
277
|
+
confidence,
|
|
278
|
+
pinned,
|
|
279
|
+
disabled,
|
|
280
|
+
created_at,
|
|
281
|
+
updated_at,
|
|
282
|
+
archived_at,
|
|
283
|
+
last_injected_at
|
|
284
|
+
) VALUES (?, ?, 'workspace', ?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?, NULL, NULL)`).run(id, input.workspaceId, ftsRowid, input.kind, input.body, tags, status, source, confidence, now, now);
|
|
285
|
+
db.prepare(`INSERT INTO memory_sources (
|
|
286
|
+
id,
|
|
287
|
+
memory_id,
|
|
288
|
+
source_type,
|
|
289
|
+
source_id,
|
|
290
|
+
source_sequence,
|
|
291
|
+
excerpt,
|
|
292
|
+
text_hash,
|
|
293
|
+
actor_agent_id_snapshot,
|
|
294
|
+
actor_name_snapshot,
|
|
295
|
+
actor_role_snapshot,
|
|
296
|
+
created_at
|
|
297
|
+
) VALUES (?, ?, 'manual', NULL, NULL, ?, ?, ?, ?, ?, ?)`).run(sourceId, id, excerptFor(input.body), hashText(input.body), input.actor.id, input.actor.name, input.actor.role, now);
|
|
298
|
+
})();
|
|
299
|
+
const created = getEntryWithSources(input.workspaceId, id);
|
|
300
|
+
if (!created)
|
|
301
|
+
throw new Error(`Memory entry disappeared after insert: ${id}`);
|
|
302
|
+
return created;
|
|
303
|
+
};
|
|
304
|
+
const logInjections = (input) => {
|
|
305
|
+
if (input.memoryIds.length === 0)
|
|
306
|
+
return [];
|
|
307
|
+
const injectedAt = Date.now();
|
|
308
|
+
const ids = input.memoryIds.map(() => randomUUID());
|
|
309
|
+
db.transaction(() => {
|
|
310
|
+
for (const [index, memoryId] of input.memoryIds.entries()) {
|
|
311
|
+
const memory = db
|
|
312
|
+
.prepare(`SELECT id
|
|
313
|
+
FROM memory_entries
|
|
314
|
+
WHERE id = ?
|
|
315
|
+
AND scope = 'workspace'
|
|
316
|
+
AND workspace_id = ?
|
|
317
|
+
LIMIT 1`)
|
|
318
|
+
.get(memoryId, input.workspaceId);
|
|
319
|
+
if (!memory) {
|
|
320
|
+
throw new MemoryEntryNotFoundError(memoryId, input.workspaceId);
|
|
321
|
+
}
|
|
322
|
+
db.prepare(`INSERT INTO memory_injections (
|
|
323
|
+
id,
|
|
324
|
+
memory_id,
|
|
325
|
+
workspace_id,
|
|
326
|
+
target_agent_id_snapshot,
|
|
327
|
+
context_type,
|
|
328
|
+
dispatch_id,
|
|
329
|
+
injected_at
|
|
330
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(ids[index], memoryId, input.workspaceId, input.targetAgentIdSnapshot ?? null, input.contextType, input.dispatchId ?? null, injectedAt);
|
|
331
|
+
db.prepare(`UPDATE memory_entries
|
|
332
|
+
SET last_injected_at = ?
|
|
333
|
+
WHERE id = ? AND workspace_id = ?`).run(injectedAt, memoryId, input.workspaceId);
|
|
334
|
+
}
|
|
335
|
+
})();
|
|
336
|
+
return ids;
|
|
337
|
+
};
|
|
338
|
+
const deleteInjections = (injectionIds) => {
|
|
339
|
+
if (injectionIds.length === 0)
|
|
340
|
+
return;
|
|
341
|
+
const placeholders = injectionIds.map(() => '?').join(', ');
|
|
342
|
+
db.transaction(() => {
|
|
343
|
+
const rows = db
|
|
344
|
+
.prepare(`SELECT DISTINCT memory_id, workspace_id
|
|
345
|
+
FROM memory_injections
|
|
346
|
+
WHERE id IN (${placeholders})`)
|
|
347
|
+
.all(...injectionIds);
|
|
348
|
+
db.prepare(`DELETE FROM memory_injections WHERE id IN (${placeholders})`).run(...injectionIds);
|
|
349
|
+
for (const row of rows) {
|
|
350
|
+
db.prepare(`UPDATE memory_entries
|
|
351
|
+
SET last_injected_at = (
|
|
352
|
+
SELECT MAX(injected_at)
|
|
353
|
+
FROM memory_injections
|
|
354
|
+
WHERE memory_id = ?
|
|
355
|
+
AND workspace_id = ?
|
|
356
|
+
)
|
|
357
|
+
WHERE id = ?
|
|
358
|
+
AND workspace_id = ?`).run(row.memory_id, row.workspace_id, row.memory_id, row.workspace_id);
|
|
359
|
+
}
|
|
360
|
+
})();
|
|
361
|
+
};
|
|
362
|
+
const archiveEntry = (workspaceId, memoryId) => {
|
|
363
|
+
const current = requireEntryWithSources(workspaceId, memoryId);
|
|
364
|
+
if (current.status === 'archived')
|
|
365
|
+
return current;
|
|
366
|
+
if (current.status !== 'active') {
|
|
367
|
+
throw new MemoryEntryStatusError(memoryId, 'active', current.status);
|
|
368
|
+
}
|
|
369
|
+
const now = Date.now();
|
|
370
|
+
const result = db
|
|
371
|
+
.prepare(`UPDATE memory_entries
|
|
372
|
+
SET status = 'archived',
|
|
373
|
+
archived_at = ?,
|
|
374
|
+
updated_at = ?
|
|
375
|
+
WHERE id = ?
|
|
376
|
+
AND scope = 'workspace'
|
|
377
|
+
AND workspace_id = ?
|
|
378
|
+
AND status = 'active'`)
|
|
379
|
+
.run(now, now, memoryId, workspaceId);
|
|
380
|
+
if (result.changes === 0)
|
|
381
|
+
throw new MemoryEntryNotFoundError(memoryId, workspaceId);
|
|
382
|
+
return requireEntryWithSources(workspaceId, memoryId);
|
|
383
|
+
};
|
|
384
|
+
const approveCandidate = (workspaceId, memoryId) => {
|
|
385
|
+
const current = requireEntryWithSources(workspaceId, memoryId);
|
|
386
|
+
if (current.status !== 'candidate') {
|
|
387
|
+
throw new MemoryEntryStatusError(memoryId, 'candidate', current.status);
|
|
388
|
+
}
|
|
389
|
+
const now = Date.now();
|
|
390
|
+
const result = db
|
|
391
|
+
.prepare(`UPDATE memory_entries
|
|
392
|
+
SET status = 'active',
|
|
393
|
+
archived_at = NULL,
|
|
394
|
+
updated_at = ?
|
|
395
|
+
WHERE id = ?
|
|
396
|
+
AND scope = 'workspace'
|
|
397
|
+
AND workspace_id = ?
|
|
398
|
+
AND status = 'candidate'`)
|
|
399
|
+
.run(now, memoryId, workspaceId);
|
|
400
|
+
if (result.changes === 0)
|
|
401
|
+
throw new MemoryEntryNotFoundError(memoryId, workspaceId);
|
|
402
|
+
return requireEntryWithSources(workspaceId, memoryId);
|
|
403
|
+
};
|
|
404
|
+
const rejectCandidate = (workspaceId, memoryId) => {
|
|
405
|
+
const current = requireEntryWithSources(workspaceId, memoryId);
|
|
406
|
+
if (current.status !== 'candidate') {
|
|
407
|
+
throw new MemoryEntryStatusError(memoryId, 'candidate', current.status);
|
|
408
|
+
}
|
|
409
|
+
const now = Date.now();
|
|
410
|
+
const result = db
|
|
411
|
+
.prepare(`UPDATE memory_entries
|
|
412
|
+
SET status = 'rejected',
|
|
413
|
+
updated_at = ?
|
|
414
|
+
WHERE id = ?
|
|
415
|
+
AND scope = 'workspace'
|
|
416
|
+
AND workspace_id = ?
|
|
417
|
+
AND status = 'candidate'`)
|
|
418
|
+
.run(now, memoryId, workspaceId);
|
|
419
|
+
if (result.changes === 0)
|
|
420
|
+
throw new MemoryEntryNotFoundError(memoryId, workspaceId);
|
|
421
|
+
return requireEntryWithSources(workspaceId, memoryId);
|
|
422
|
+
};
|
|
423
|
+
const setPinned = (workspaceId, memoryId, pinned) => {
|
|
424
|
+
const current = requireEntryWithSources(workspaceId, memoryId);
|
|
425
|
+
if (current.status !== 'active') {
|
|
426
|
+
throw new MemoryEntryStatusError(memoryId, 'active', current.status);
|
|
427
|
+
}
|
|
428
|
+
const now = Date.now();
|
|
429
|
+
const result = db
|
|
430
|
+
.prepare(`UPDATE memory_entries
|
|
431
|
+
SET pinned = ?,
|
|
432
|
+
updated_at = ?
|
|
433
|
+
WHERE id = ?
|
|
434
|
+
AND scope = 'workspace'
|
|
435
|
+
AND workspace_id = ?
|
|
436
|
+
AND status = 'active'`)
|
|
437
|
+
.run(pinned ? 1 : 0, now, memoryId, workspaceId);
|
|
438
|
+
if (result.changes === 0)
|
|
439
|
+
throw new MemoryEntryNotFoundError(memoryId, workspaceId);
|
|
440
|
+
return requireEntryWithSources(workspaceId, memoryId);
|
|
441
|
+
};
|
|
442
|
+
const setDisabled = (workspaceId, memoryId, disabled) => {
|
|
443
|
+
const current = requireEntryWithSources(workspaceId, memoryId);
|
|
444
|
+
if (current.status !== 'active') {
|
|
445
|
+
throw new MemoryEntryStatusError(memoryId, 'active', current.status);
|
|
446
|
+
}
|
|
447
|
+
const now = Date.now();
|
|
448
|
+
const result = db
|
|
449
|
+
.prepare(`UPDATE memory_entries
|
|
450
|
+
SET disabled = ?,
|
|
451
|
+
updated_at = ?
|
|
452
|
+
WHERE id = ?
|
|
453
|
+
AND scope = 'workspace'
|
|
454
|
+
AND workspace_id = ?
|
|
455
|
+
AND status = 'active'`)
|
|
456
|
+
.run(disabled ? 1 : 0, now, memoryId, workspaceId);
|
|
457
|
+
if (result.changes === 0)
|
|
458
|
+
throw new MemoryEntryNotFoundError(memoryId, workspaceId);
|
|
459
|
+
return requireEntryWithSources(workspaceId, memoryId);
|
|
460
|
+
};
|
|
461
|
+
const deleteWorkspaceMemories = (workspaceId) => {
|
|
462
|
+
db.transaction(() => {
|
|
463
|
+
db.prepare('DELETE FROM memory_injections WHERE workspace_id = ?').run(workspaceId);
|
|
464
|
+
const rows = db
|
|
465
|
+
.prepare(`SELECT id
|
|
466
|
+
FROM memory_entries
|
|
467
|
+
WHERE scope = 'workspace'
|
|
468
|
+
AND workspace_id = ?`)
|
|
469
|
+
.all(workspaceId);
|
|
470
|
+
for (const row of rows) {
|
|
471
|
+
db.prepare('DELETE FROM memory_sources WHERE memory_id = ?').run(row.id);
|
|
472
|
+
db.prepare('DELETE FROM memory_injections WHERE memory_id = ?').run(row.id);
|
|
473
|
+
}
|
|
474
|
+
db.prepare(`DELETE FROM memory_entries
|
|
475
|
+
WHERE scope = 'workspace'
|
|
476
|
+
AND workspace_id = ?`).run(workspaceId);
|
|
477
|
+
})();
|
|
478
|
+
};
|
|
479
|
+
const listInjectionsForDispatch = (workspaceId, dispatchId) => db
|
|
480
|
+
.prepare(`SELECT *
|
|
481
|
+
FROM memory_injections
|
|
482
|
+
WHERE workspace_id = ?
|
|
483
|
+
AND dispatch_id = ?
|
|
484
|
+
ORDER BY injected_at ASC, id ASC`)
|
|
485
|
+
.all(workspaceId, dispatchId).map((row) => ({
|
|
486
|
+
contextType: row.context_type,
|
|
487
|
+
dispatchId: row.dispatch_id,
|
|
488
|
+
id: row.id,
|
|
489
|
+
injectedAt: row.injected_at,
|
|
490
|
+
memory: requireEntryWithSources(workspaceId, row.memory_id),
|
|
491
|
+
memoryId: row.memory_id,
|
|
492
|
+
targetAgentIdSnapshot: row.target_agent_id_snapshot,
|
|
493
|
+
workspaceId: row.workspace_id,
|
|
494
|
+
}));
|
|
495
|
+
return {
|
|
496
|
+
addEntry,
|
|
497
|
+
approveCandidate,
|
|
498
|
+
archiveEntry,
|
|
499
|
+
deleteWorkspaceMemories,
|
|
500
|
+
deleteInjections,
|
|
501
|
+
getEntryWithSources,
|
|
502
|
+
listDigestEntries,
|
|
503
|
+
listExportEntries,
|
|
504
|
+
listAllEntries,
|
|
505
|
+
listEntries,
|
|
506
|
+
listInjectionsForDispatch,
|
|
507
|
+
logInjections,
|
|
508
|
+
rejectCandidate,
|
|
509
|
+
searchEntries,
|
|
510
|
+
setDisabled,
|
|
511
|
+
setPinned,
|
|
512
|
+
};
|
|
513
|
+
};
|
|
@@ -48,6 +48,7 @@ export interface TeamOperationsInput {
|
|
|
48
48
|
dismissEphemeralWorker?: (workspaceId: string, workerId: string) => void;
|
|
49
49
|
}
|
|
50
50
|
export interface DispatchTaskInput {
|
|
51
|
+
autoStartWorker?: boolean;
|
|
51
52
|
fromAgentId?: string;
|
|
52
53
|
hivePort?: string;
|
|
53
54
|
workflowRunId?: string;
|
|
@@ -59,7 +60,7 @@ export interface ReportTaskInput {
|
|
|
59
60
|
artifacts?: string[];
|
|
60
61
|
dispatchId?: string;
|
|
61
62
|
requireActiveRun?: boolean;
|
|
62
|
-
status?:
|
|
63
|
+
status?: 'success' | 'failed';
|
|
63
64
|
text?: string;
|
|
64
65
|
}
|
|
65
66
|
export interface StatusTaskInput {
|
|
@@ -75,6 +76,7 @@ export interface ReportTaskResult {
|
|
|
75
76
|
dispatch: DispatchRecord | null;
|
|
76
77
|
forwardError: string | null;
|
|
77
78
|
forwarded: boolean;
|
|
79
|
+
pendingWarning?: string;
|
|
78
80
|
}
|
|
79
81
|
export declare const createTeamOperations: ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, markDispatchSubmitted, reportOutbox, notifyWebhook, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, }: TeamOperationsInput) => {
|
|
80
82
|
cancelTask(workspaceId: string, dispatchId: string, input: CancelTaskInput): {
|
|
@@ -89,11 +91,13 @@ export declare const createTeamOperations: ({ agentRuntime, createDispatch, dele
|
|
|
89
91
|
}>;
|
|
90
92
|
recordUserInput(workspaceId: string, orchestratorId: string, text: string): void;
|
|
91
93
|
statusTask(workspaceId: string, workerId: string, input?: StatusTaskInput): {
|
|
94
|
+
pendingWarning?: string;
|
|
92
95
|
dispatch: null;
|
|
93
96
|
forwardError: string | null;
|
|
94
97
|
forwarded: boolean;
|
|
95
98
|
};
|
|
96
99
|
reportTask(workspaceId: string, workerId: string, input?: ReportTaskInput): {
|
|
100
|
+
pendingWarning?: string;
|
|
97
101
|
dispatch: DispatchRecord;
|
|
98
102
|
forwardError: string | null;
|
|
99
103
|
forwarded: boolean;
|
|
@@ -25,6 +25,10 @@ export const formatUnknownWorkerError = (workerName, roster) => {
|
|
|
25
25
|
].join('\n');
|
|
26
26
|
};
|
|
27
27
|
const reportForwardErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
28
|
+
const buildPendingWarning = (workerName, pendingTaskCount, action) => pendingTaskCount > 0
|
|
29
|
+
? `Hive recorded the ${action}, but ${workerName} still has ${pendingTaskCount} open dispatch${pendingTaskCount === 1 ? '' : 'es'}. ` +
|
|
30
|
+
'A worker stays working until every dispatch is closed with `team report --dispatch <id>` or `team cancel --dispatch <id>`.'
|
|
31
|
+
: undefined;
|
|
28
32
|
export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispatch, deleteMessage, findOpenDispatch, findOpenDispatchById, insertMessage, markDispatchCancelled, markDispatchReportedByWorker, markDispatchSubmitted, reportOutbox, notifyWebhook, workflowDispatchAwaiter, workspaceStore, dismissEphemeralWorker, }) => {
|
|
29
33
|
// Best-effort redelivery of reports a prior orchestrator outage stranded.
|
|
30
34
|
// Called when a fresh report confirms the orchestrator is reachable and
|
|
@@ -82,6 +86,9 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
|
|
|
82
86
|
}
|
|
83
87
|
};
|
|
84
88
|
const dispatchTask = async (workspaceId, workerId, text, input = {}) => {
|
|
89
|
+
const fromAgentId = input.fromAgentId;
|
|
90
|
+
const sender = fromAgentId ? workspaceStore.getAgent(workspaceId, fromAgentId) : undefined;
|
|
91
|
+
const worker = workspaceStore.getWorker(workspaceId, workerId);
|
|
85
92
|
const message = createSendMessage(workspaceId, workerId, text, input.fromAgentId);
|
|
86
93
|
const messageHandle = insertMessage(message);
|
|
87
94
|
let dispatch;
|
|
@@ -92,8 +99,8 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
|
|
|
92
99
|
toAgentId: workerId,
|
|
93
100
|
workspaceId,
|
|
94
101
|
};
|
|
95
|
-
if (
|
|
96
|
-
dispatchInput.fromAgentId =
|
|
102
|
+
if (fromAgentId)
|
|
103
|
+
dispatchInput.fromAgentId = fromAgentId;
|
|
97
104
|
if (input.workflowRunId !== undefined)
|
|
98
105
|
dispatchInput.workflowRunId = input.workflowRunId;
|
|
99
106
|
if (input.stepIndex !== undefined)
|
|
@@ -104,10 +111,17 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
|
|
|
104
111
|
dispatchInput.label = input.label;
|
|
105
112
|
dispatch = createDispatch(dispatchInput);
|
|
106
113
|
const dispatchId = dispatch.id;
|
|
107
|
-
if (
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
if (fromAgentId && sender) {
|
|
115
|
+
const shouldAutoStartWorker = input.autoStartWorker !== false;
|
|
116
|
+
const hadActiveRun = Boolean(agentRuntime.getActiveRunByAgentId(workspaceId, workerId));
|
|
117
|
+
if (!hadActiveRun && shouldAutoStartWorker) {
|
|
118
|
+
await ensureWorkerRun(workspaceId, workerId, input.hivePort ?? '');
|
|
119
|
+
}
|
|
120
|
+
if (!shouldAutoStartWorker && !agentRuntime.getActiveRunByAgentId(workspaceId, workerId)) {
|
|
121
|
+
workspaceStore.markTaskDispatched(workspaceId, workerId);
|
|
122
|
+
pendingMarked = true;
|
|
123
|
+
return dispatch;
|
|
124
|
+
}
|
|
111
125
|
const isWorkflowDispatch = input.workflowRunId !== undefined || input.fromAgentId === getWorkflowAgentId(workspaceId);
|
|
112
126
|
markDispatchSubmitted(dispatchId);
|
|
113
127
|
workspaceStore.markTaskDispatched(workspaceId, workerId);
|
|
@@ -204,14 +218,12 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
|
|
|
204
218
|
if (!worker) {
|
|
205
219
|
throw new ConflictError(formatUnknownWorkerError(workerName, roster));
|
|
206
220
|
}
|
|
207
|
-
/* Capture the active-run state *before* dispatchTask runs
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
change into a transparent one. Internal calls without `fromAgentId`
|
|
212
|
-
only queue work and do not wake a PTY, so they must not report a
|
|
213
|
-
restart. */
|
|
221
|
+
/* Capture the active-run state *before* dispatchTask runs. Internal
|
|
222
|
+
workflow calls may auto-start a stopped worker; explicit CLI
|
|
223
|
+
`team send` passes autoStartWorker:false so the worker remains
|
|
224
|
+
stopped with a queued dispatch until the user restarts it. */
|
|
214
225
|
const restartedWorker = input.fromAgentId !== undefined &&
|
|
226
|
+
input.autoStartWorker !== false &&
|
|
215
227
|
!agentRuntime.getActiveRunByAgentId(workspaceId, worker.id);
|
|
216
228
|
const dispatch = await dispatchTask(workspaceId, worker.id, text, input);
|
|
217
229
|
return Object.assign(dispatch, { restartedWorker });
|
|
@@ -241,7 +253,13 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
|
|
|
241
253
|
console.error('[hive] swallowed:teamStatus.forward', error);
|
|
242
254
|
}
|
|
243
255
|
}
|
|
244
|
-
|
|
256
|
+
const pendingWarning = buildPendingWarning(worker.name, worker.pendingTaskCount, 'status update');
|
|
257
|
+
return {
|
|
258
|
+
dispatch: null,
|
|
259
|
+
forwardError,
|
|
260
|
+
forwarded,
|
|
261
|
+
...(pendingWarning ? { pendingWarning } : {}),
|
|
262
|
+
};
|
|
245
263
|
}
|
|
246
264
|
catch (error) {
|
|
247
265
|
deleteMessage(messageHandle);
|
|
@@ -270,8 +288,10 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
|
|
|
270
288
|
throw new ConflictError(`No open dispatch for worker: ${worker.name}`);
|
|
271
289
|
}
|
|
272
290
|
workspaceStore.markTaskReported(workspaceId, workerId);
|
|
291
|
+
const remainingPendingTaskCount = workspaceStore.getWorker(workspaceId, workerId).pendingTaskCount;
|
|
273
292
|
let forwardError = null;
|
|
274
293
|
let forwarded = false;
|
|
294
|
+
const pendingWarning = buildPendingWarning(worker.name, remainingPendingTaskCount, 'report');
|
|
275
295
|
// Workflow-sourced dispatches: the source is the in-process runner, not a
|
|
276
296
|
// PTY. Resolve its awaiting Promise instead of injecting into orchestrator
|
|
277
297
|
// stdin (which would do nothing — `__workflow__` has no PTY).
|
|
@@ -288,7 +308,12 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
|
|
|
288
308
|
forwardError = reportForwardErrorMessage(error);
|
|
289
309
|
console.error('[hive] swallowed:teamReport.workflowForward', error);
|
|
290
310
|
}
|
|
291
|
-
return {
|
|
311
|
+
return {
|
|
312
|
+
dispatch,
|
|
313
|
+
forwardError,
|
|
314
|
+
forwarded,
|
|
315
|
+
...(pendingWarning ? { pendingWarning } : {}),
|
|
316
|
+
};
|
|
292
317
|
}
|
|
293
318
|
// Real worker reported (not a workflow-internal step) — fire the
|
|
294
319
|
// outbound completion webhook. Best-effort; never blocks the report.
|
|
@@ -369,7 +394,12 @@ export const createTeamOperations = ({ agentRuntime, createDispatch, deleteDispa
|
|
|
369
394
|
}
|
|
370
395
|
});
|
|
371
396
|
}
|
|
372
|
-
return {
|
|
397
|
+
return {
|
|
398
|
+
dispatch,
|
|
399
|
+
forwardError,
|
|
400
|
+
forwarded,
|
|
401
|
+
...(pendingWarning ? { pendingWarning } : {}),
|
|
402
|
+
};
|
|
373
403
|
}
|
|
374
404
|
catch (error) {
|
|
375
405
|
deleteMessage(messageHandle);
|