funolio-agent 0.17.8 → 1.0.3
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/dist/approval.d.ts +7 -6
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +40 -10
- package/dist/approval.js.map +1 -1
- package/dist/auth/anthropic-subscription.d.ts +9 -29
- package/dist/auth/anthropic-subscription.d.ts.map +1 -1
- package/dist/auth/anthropic-subscription.js +12 -133
- package/dist/auth/anthropic-subscription.js.map +1 -1
- package/dist/auth/auto-detect.d.ts +28 -6
- package/dist/auth/auto-detect.d.ts.map +1 -1
- package/dist/auth/auto-detect.js +52 -205
- package/dist/auth/auto-detect.js.map +1 -1
- package/dist/auth/credential-reader.d.ts +24 -4
- package/dist/auth/credential-reader.d.ts.map +1 -1
- package/dist/auth/credential-reader.js +31 -256
- package/dist/auth/credential-reader.js.map +1 -1
- package/dist/auth/credential-status.d.ts +7 -27
- package/dist/auth/credential-status.d.ts.map +1 -1
- package/dist/auth/credential-status.js +7 -95
- package/dist/auth/credential-status.js.map +1 -1
- package/dist/auth/index.d.ts +9 -2
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +10 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/subscription-runtime.d.ts +19 -23
- package/dist/auth/subscription-runtime.d.ts.map +1 -1
- package/dist/auth/subscription-runtime.js +24 -292
- package/dist/auth/subscription-runtime.js.map +1 -1
- package/dist/auth/token-refresh.d.ts +19 -28
- package/dist/auth/token-refresh.d.ts.map +1 -1
- package/dist/auth/token-refresh.js +26 -464
- package/dist/auth/token-refresh.js.map +1 -1
- package/dist/backfill.js +2 -2
- package/dist/backfill.js.map +1 -1
- package/dist/bot-manager.d.ts +5 -6
- package/dist/bot-manager.d.ts.map +1 -1
- package/dist/bot-manager.js +20 -62
- package/dist/bot-manager.js.map +1 -1
- package/dist/clerk-model.d.ts +0 -1
- package/dist/clerk-model.d.ts.map +1 -1
- package/dist/clerk-model.js +24 -50
- package/dist/clerk-model.js.map +1 -1
- package/dist/commands/configure-provider.js +2 -2
- package/dist/commands/configure-provider.js.map +1 -1
- package/dist/commands/configure.d.ts.map +1 -1
- package/dist/commands/configure.js +10 -14
- package/dist/commands/configure.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +51 -229
- package/dist/commands/start.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts +6 -1
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +87 -18
- package/dist/context-compressor.js.map +1 -1
- package/dist/context-window.d.ts +2 -8
- package/dist/context-window.d.ts.map +1 -1
- package/dist/context-window.js +4 -4
- package/dist/context-window.js.map +1 -1
- package/dist/eval/orchestrator-front-door-replay.js +43 -2
- package/dist/eval/orchestrator-front-door-replay.js.map +1 -1
- package/dist/eval/orchestrator-todo-dispatch-replay.d.ts +2 -0
- package/dist/eval/orchestrator-todo-dispatch-replay.d.ts.map +1 -0
- package/dist/eval/orchestrator-todo-dispatch-replay.js +253 -0
- package/dist/eval/orchestrator-todo-dispatch-replay.js.map +1 -0
- package/dist/eval/orchestrator-todo-planning-replay.d.ts +2 -0
- package/dist/eval/orchestrator-todo-planning-replay.d.ts.map +1 -0
- package/dist/eval/orchestrator-todo-planning-replay.js +247 -0
- package/dist/eval/orchestrator-todo-planning-replay.js.map +1 -0
- package/dist/eval/policy-detection-replay.d.ts +2 -0
- package/dist/eval/policy-detection-replay.d.ts.map +1 -0
- package/dist/eval/policy-detection-replay.js +122 -0
- package/dist/eval/policy-detection-replay.js.map +1 -0
- package/dist/eval/todo-worker-runtime-replay.d.ts +2 -0
- package/dist/eval/todo-worker-runtime-replay.d.ts.map +1 -0
- package/dist/eval/todo-worker-runtime-replay.js +520 -0
- package/dist/eval/todo-worker-runtime-replay.js.map +1 -0
- package/dist/integration-tokens.d.ts +6 -0
- package/dist/integration-tokens.d.ts.map +1 -1
- package/dist/integration-tokens.js +43 -0
- package/dist/integration-tokens.js.map +1 -1
- package/dist/local-data.d.ts +134 -1
- package/dist/local-data.d.ts.map +1 -1
- package/dist/local-data.js +711 -18
- package/dist/local-data.js.map +1 -1
- package/dist/local-db.d.ts.map +1 -1
- package/dist/local-db.js +216 -12
- package/dist/local-db.js.map +1 -1
- package/dist/local-funnel.d.ts.map +1 -1
- package/dist/local-funnel.js +7 -0
- package/dist/local-funnel.js.map +1 -1
- package/dist/local-server.d.ts.map +1 -1
- package/dist/local-server.js +119 -96
- package/dist/local-server.js.map +1 -1
- package/dist/mcp/bridge-server.d.ts.map +1 -1
- package/dist/mcp/bridge-server.js +8 -2
- package/dist/mcp/bridge-server.js.map +1 -1
- package/dist/mcp/manager.d.ts +5 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +131 -2
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/sync-cli-config.d.ts +5 -0
- package/dist/mcp/sync-cli-config.d.ts.map +1 -1
- package/dist/mcp/sync-cli-config.js +10 -2
- package/dist/mcp/sync-cli-config.js.map +1 -1
- package/dist/message-loop.d.ts +1 -10
- package/dist/message-loop.d.ts.map +1 -1
- package/dist/message-loop.js +179 -250
- package/dist/message-loop.js.map +1 -1
- package/dist/mqtt-client.d.ts +44 -0
- package/dist/mqtt-client.d.ts.map +1 -1
- package/dist/mqtt-client.js +2 -2
- package/dist/mqtt-client.js.map +1 -1
- package/dist/orchestration/front-door-policy.d.ts +26 -9
- package/dist/orchestration/front-door-policy.d.ts.map +1 -1
- package/dist/orchestration/front-door-policy.js +242 -69
- package/dist/orchestration/front-door-policy.js.map +1 -1
- package/dist/orchestration/orchestrator-blocked-prompt.d.ts +18 -0
- package/dist/orchestration/orchestrator-blocked-prompt.d.ts.map +1 -0
- package/dist/orchestration/orchestrator-blocked-prompt.js +46 -0
- package/dist/orchestration/orchestrator-blocked-prompt.js.map +1 -0
- package/dist/orchestration/orchestrator-final-response-prompt.d.ts +10 -0
- package/dist/orchestration/orchestrator-final-response-prompt.d.ts.map +1 -0
- package/dist/orchestration/orchestrator-final-response-prompt.js +39 -0
- package/dist/orchestration/orchestrator-final-response-prompt.js.map +1 -0
- package/dist/orchestration/orchestrator-operating-prompt.d.ts +11 -0
- package/dist/orchestration/orchestrator-operating-prompt.d.ts.map +1 -1
- package/dist/orchestration/orchestrator-operating-prompt.js +106 -36
- package/dist/orchestration/orchestrator-operating-prompt.js.map +1 -1
- package/dist/orchestration/policy-prompt.d.ts +6 -0
- package/dist/orchestration/policy-prompt.d.ts.map +1 -0
- package/dist/orchestration/policy-prompt.js +40 -0
- package/dist/orchestration/policy-prompt.js.map +1 -0
- package/dist/orchestration/worker-operating-prompt.d.ts +16 -0
- package/dist/orchestration/worker-operating-prompt.d.ts.map +1 -0
- package/dist/orchestration/worker-operating-prompt.js +75 -0
- package/dist/orchestration/worker-operating-prompt.js.map +1 -0
- package/dist/orchestrator.d.ts +19 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +614 -54
- package/dist/orchestrator.js.map +1 -1
- package/dist/policy-detection.d.ts +40 -0
- package/dist/policy-detection.d.ts.map +1 -0
- package/dist/policy-detection.js +298 -0
- package/dist/policy-detection.js.map +1 -0
- package/dist/providers/anthropic.d.ts +0 -5
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +34 -51
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/claude-cli.d.ts.map +1 -1
- package/dist/providers/claude-cli.js +11 -2
- package/dist/providers/claude-cli.js.map +1 -1
- package/dist/providers/codex-cli.d.ts.map +1 -1
- package/dist/providers/codex-cli.js +121 -2
- package/dist/providers/codex-cli.js.map +1 -1
- package/dist/providers/index.d.ts +6 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/summarization-pipeline.d.ts +1 -4
- package/dist/summarization-pipeline.d.ts.map +1 -1
- package/dist/summarization-pipeline.js +43 -56
- package/dist/summarization-pipeline.js.map +1 -1
- package/dist/tools/analyze-image.js +2 -2
- package/dist/tools/analyze-image.js.map +1 -1
- package/dist/tools/edit-file.d.ts.map +1 -1
- package/dist/tools/edit-file.js +16 -1
- package/dist/tools/edit-file.js.map +1 -1
- package/dist/tools/git-tools.d.ts.map +1 -1
- package/dist/tools/git-tools.js +32 -1
- package/dist/tools/git-tools.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-directory.d.ts.map +1 -1
- package/dist/tools/list-directory.js +23 -0
- package/dist/tools/list-directory.js.map +1 -1
- package/dist/tools/notify-user.d.ts.map +1 -1
- package/dist/tools/notify-user.js +15 -1
- package/dist/tools/notify-user.js.map +1 -1
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +3 -1
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/request-mcp-install.js +5 -5
- package/dist/tools/request-mcp-install.js.map +1 -1
- package/dist/tools/run-command.d.ts.map +1 -1
- package/dist/tools/run-command.js +16 -4
- package/dist/tools/run-command.js.map +1 -1
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +6 -0
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/schedule-task.d.ts.map +1 -1
- package/dist/tools/schedule-task.js +37 -0
- package/dist/tools/schedule-task.js.map +1 -1
- package/dist/tools/search-codebase.d.ts.map +1 -1
- package/dist/tools/search-codebase.js +251 -32
- package/dist/tools/search-codebase.js.map +1 -1
- package/dist/tools/todo-tasks.d.ts +2 -0
- package/dist/tools/todo-tasks.d.ts.map +1 -1
- package/dist/tools/todo-tasks.js +203 -6
- package/dist/tools/todo-tasks.js.map +1 -1
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +21 -2
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +38 -34
- package/dist/tools/web-search.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/wizard-state.d.ts.map +1 -1
- package/dist/wizard-state.js +30 -8
- package/dist/wizard-state.js.map +1 -1
- package/dist/wizard-support.d.ts +2 -2
- package/dist/wizard-support.d.ts.map +1 -1
- package/dist/wizard-support.js +80 -93
- package/dist/wizard-support.js.map +1 -1
- package/dist/workflow-engine.d.ts +3 -0
- package/dist/workflow-engine.d.ts.map +1 -1
- package/dist/workflow-engine.js +111 -82
- package/dist/workflow-engine.js.map +1 -1
- package/package.json +6 -1
package/dist/local-data.js
CHANGED
|
@@ -49,6 +49,8 @@ exports.listProviderConnections = listProviderConnections;
|
|
|
49
49
|
exports.getProviderConnection = getProviderConnection;
|
|
50
50
|
exports.findProviderConnection = findProviderConnection;
|
|
51
51
|
exports.updateProviderConnection = updateProviderConnection;
|
|
52
|
+
exports.normalizeCliProviderConnections = normalizeCliProviderConnections;
|
|
53
|
+
exports.pruneUnsupportedProviderConnections = pruneUnsupportedProviderConnections;
|
|
52
54
|
exports.upsertOauthProviderConnection = upsertOauthProviderConnection;
|
|
53
55
|
exports.deleteProviderConnection = deleteProviderConnection;
|
|
54
56
|
exports.deleteAgentProfile = deleteAgentProfile;
|
|
@@ -82,6 +84,7 @@ exports.upsertMemoryFact = upsertMemoryFact;
|
|
|
82
84
|
exports.searchMemoryFacts = searchMemoryFacts;
|
|
83
85
|
exports.listMemoryFacts = listMemoryFacts;
|
|
84
86
|
exports.deleteMemoryFact = deleteMemoryFact;
|
|
87
|
+
exports.getUserProfileFacts = getUserProfileFacts;
|
|
85
88
|
exports.upsertEntity = upsertEntity;
|
|
86
89
|
exports.getEntityGraph = getEntityGraph;
|
|
87
90
|
exports.upsertEntityRelationship = upsertEntityRelationship;
|
|
@@ -111,6 +114,8 @@ exports.getEffectiveOrchestrationPolicy = getEffectiveOrchestrationPolicy;
|
|
|
111
114
|
exports.getPendingOrchestrationPolicy = getPendingOrchestrationPolicy;
|
|
112
115
|
exports.setPendingOrchestrationPolicy = setPendingOrchestrationPolicy;
|
|
113
116
|
exports.clearPendingOrchestrationPolicy = clearPendingOrchestrationPolicy;
|
|
117
|
+
exports.getPolicyDetectionEventForMessage = getPolicyDetectionEventForMessage;
|
|
118
|
+
exports.recordPolicyDetectionEvent = recordPolicyDetectionEvent;
|
|
114
119
|
exports.getOrchestrationCheckpoint = getOrchestrationCheckpoint;
|
|
115
120
|
exports.setOrchestrationCheckpoint = setOrchestrationCheckpoint;
|
|
116
121
|
exports.clearOrchestrationCheckpoint = clearOrchestrationCheckpoint;
|
|
@@ -124,12 +129,18 @@ exports.addTodoTask = addTodoTask;
|
|
|
124
129
|
exports.editTodoTask = editTodoTask;
|
|
125
130
|
exports.checkTodoTask = checkTodoTask;
|
|
126
131
|
exports.completeTodoTaskByUser = completeTodoTaskByUser;
|
|
132
|
+
exports.listTodoArtifactsForTask = listTodoArtifactsForTask;
|
|
133
|
+
exports.completeTodoTaskByWorker = completeTodoTaskByWorker;
|
|
134
|
+
exports.blockTodoTaskByWorker = blockTodoTaskByWorker;
|
|
127
135
|
exports.deleteTodoTask = deleteTodoTask;
|
|
128
136
|
exports.reorderTodoTask = reorderTodoTask;
|
|
129
137
|
exports.listTodoAudit = listTodoAudit;
|
|
130
138
|
exports.moveNullTodoTasksToUnassigned = moveNullTodoTasksToUnassigned;
|
|
131
139
|
exports.getLatestActiveTodoForConversation = getLatestActiveTodoForConversation;
|
|
140
|
+
exports.getNextActiveTodoForConversation = getNextActiveTodoForConversation;
|
|
141
|
+
exports.listCompletedTodoTasksForConversation = listCompletedTodoTasksForConversation;
|
|
132
142
|
exports.createProject = createProject;
|
|
143
|
+
exports.upsertProject = upsertProject;
|
|
133
144
|
exports.getProject = getProject;
|
|
134
145
|
exports.getUnassignedProject = getUnassignedProject;
|
|
135
146
|
exports.ensureUnassignedProject = ensureUnassignedProject;
|
|
@@ -197,6 +208,7 @@ exports.resetDerivedData = resetDerivedData;
|
|
|
197
208
|
const local_db_1 = require("./local-db");
|
|
198
209
|
const memory_text_1 = require("./memory-text");
|
|
199
210
|
const memory_extraction_1 = require("./memory-extraction");
|
|
211
|
+
const orchestrator_blocked_prompt_1 = require("./orchestration/orchestrator-blocked-prompt");
|
|
200
212
|
const crypto = __importStar(require("crypto"));
|
|
201
213
|
const default_tool_profile_1 = require("./default-tool-profile");
|
|
202
214
|
let _db = null;
|
|
@@ -418,6 +430,8 @@ function setOrchestratorBot(botId) {
|
|
|
418
430
|
transaction();
|
|
419
431
|
return db.prepare('SELECT * FROM agent_profile WHERE id = ?').get(botId);
|
|
420
432
|
}
|
|
433
|
+
const CLI_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli']);
|
|
434
|
+
const SUPPORTED_PROVIDER_ACCESS_MODES = new Set(['api', 'cli']);
|
|
421
435
|
function createProviderConnection(p) {
|
|
422
436
|
const db = getDb();
|
|
423
437
|
const id = newId();
|
|
@@ -481,7 +495,93 @@ function updateProviderConnection(id, fields) {
|
|
|
481
495
|
db.prepare(`UPDATE provider_connection SET ${sets.join(', ')} WHERE id = ?`).run(...vals);
|
|
482
496
|
return db.prepare('SELECT * FROM provider_connection WHERE id = ?').get(id);
|
|
483
497
|
}
|
|
498
|
+
function normalizeCliProviderConnections() {
|
|
499
|
+
const db = getDb();
|
|
500
|
+
const rows = db.prepare(`SELECT * FROM provider_connection WHERE provider_id IN ('claude-cli', 'codex-cli')`).all();
|
|
501
|
+
const updatedIds = [];
|
|
502
|
+
const tx = db.transaction(() => {
|
|
503
|
+
for (const row of rows) {
|
|
504
|
+
const needsNormalization = row.access_mode !== 'cli'
|
|
505
|
+
|| row.auth_type !== 'cli'
|
|
506
|
+
|| !!row.api_key_enc
|
|
507
|
+
|| !!row.oauth_token
|
|
508
|
+
|| !!row.oauth_refresh_token
|
|
509
|
+
|| row.oauth_expires_at != null;
|
|
510
|
+
if (!needsNormalization)
|
|
511
|
+
continue;
|
|
512
|
+
db.prepare(`
|
|
513
|
+
UPDATE provider_connection
|
|
514
|
+
SET access_mode = 'cli',
|
|
515
|
+
auth_type = 'cli',
|
|
516
|
+
api_key_enc = NULL,
|
|
517
|
+
oauth_token = NULL,
|
|
518
|
+
oauth_refresh_token = NULL,
|
|
519
|
+
oauth_expires_at = NULL,
|
|
520
|
+
updated_at = datetime('now')
|
|
521
|
+
WHERE id = ?
|
|
522
|
+
`).run(row.id);
|
|
523
|
+
updatedIds.push(row.id);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
tx();
|
|
527
|
+
return { updatedIds };
|
|
528
|
+
}
|
|
529
|
+
function pruneUnsupportedProviderConnections() {
|
|
530
|
+
const db = getDb();
|
|
531
|
+
const rows = listProviderConnections().filter((row) => !SUPPORTED_PROVIDER_ACCESS_MODES.has(row.access_mode));
|
|
532
|
+
if (rows.length === 0)
|
|
533
|
+
return { deletedIds: [], skippedIds: [] };
|
|
534
|
+
const referencedByBots = new Set(db.prepare(`SELECT provider_connection_id
|
|
535
|
+
FROM agent_profile
|
|
536
|
+
WHERE provider_connection_id IS NOT NULL`).all().map((row) => row.provider_connection_id));
|
|
537
|
+
const clerkConfig = getJsonSetting('clerk.config');
|
|
538
|
+
const protectedIds = new Set([
|
|
539
|
+
...referencedByBots,
|
|
540
|
+
...(clerkConfig?.providerConnectionId ? [clerkConfig.providerConnectionId] : []),
|
|
541
|
+
]);
|
|
542
|
+
const deletedIds = [];
|
|
543
|
+
const skippedIds = [];
|
|
544
|
+
const tx = db.transaction(() => {
|
|
545
|
+
for (const row of rows) {
|
|
546
|
+
if (protectedIds.has(row.id)) {
|
|
547
|
+
skippedIds.push(row.id);
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
db.prepare('DELETE FROM provider_connection WHERE id = ?').run(row.id);
|
|
551
|
+
deletedIds.push(row.id);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
tx();
|
|
555
|
+
return { deletedIds, skippedIds };
|
|
556
|
+
}
|
|
484
557
|
function upsertOauthProviderConnection(input) {
|
|
558
|
+
if (CLI_PROVIDER_IDS.has(input.providerId)) {
|
|
559
|
+
const existing = findProviderConnection(input.providerId, 'cli')
|
|
560
|
+
|| listProviderConnections().find((row) => row.provider_id === input.providerId);
|
|
561
|
+
if (existing) {
|
|
562
|
+
return updateProviderConnection(existing.id, {
|
|
563
|
+
accessMode: 'cli',
|
|
564
|
+
authType: 'cli',
|
|
565
|
+
apiKeyEnc: null,
|
|
566
|
+
oauthToken: null,
|
|
567
|
+
oauthRefreshToken: null,
|
|
568
|
+
oauthExpiresAt: null,
|
|
569
|
+
...(input.defaultModel !== undefined ? { defaultModel: input.defaultModel } : {}),
|
|
570
|
+
...(input.label !== undefined ? { label: input.label ?? undefined } : {}),
|
|
571
|
+
status: 'configured',
|
|
572
|
+
lastError: null,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
return createProviderConnection({
|
|
576
|
+
providerId: input.providerId,
|
|
577
|
+
accessMode: 'cli',
|
|
578
|
+
authType: 'cli',
|
|
579
|
+
...(input.label !== undefined ? { label: input.label ?? undefined } : {}),
|
|
580
|
+
...(input.defaultModel !== undefined ? { defaultModel: input.defaultModel ?? undefined } : {}),
|
|
581
|
+
status: 'configured',
|
|
582
|
+
lastError: undefined,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
485
585
|
const existing = findProviderConnection(input.providerId, 'oauth')
|
|
486
586
|
|| listProviderConnections().find((row) => row.provider_id === input.providerId && row.auth_type === 'oauth');
|
|
487
587
|
if (existing) {
|
|
@@ -1098,6 +1198,20 @@ function listMemoryFacts(opts) {
|
|
|
1098
1198
|
function deleteMemoryFact(id) {
|
|
1099
1199
|
return getDb().prepare('DELETE FROM memory_fact WHERE id = ?').run(id).changes > 0;
|
|
1100
1200
|
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Get user-profile facts (person entities) for injection into the system prompt.
|
|
1203
|
+
* Returns high-confidence facts about people (address, preferences, etc.)
|
|
1204
|
+
* limited to ~500 tokens worth of content.
|
|
1205
|
+
*/
|
|
1206
|
+
function getUserProfileFacts(agentId, limit = 15) {
|
|
1207
|
+
const db = getDb();
|
|
1208
|
+
return db.prepare(`
|
|
1209
|
+
SELECT * FROM memory_fact
|
|
1210
|
+
WHERE agent_id = ? AND entity_type = 'person'
|
|
1211
|
+
ORDER BY confidence DESC, updated_at DESC
|
|
1212
|
+
LIMIT ?
|
|
1213
|
+
`).all(agentId, limit);
|
|
1214
|
+
}
|
|
1101
1215
|
function upsertEntity(agentId, name, type) {
|
|
1102
1216
|
const db = getDb();
|
|
1103
1217
|
const existing = db.prepare('SELECT * FROM entity WHERE agent_id = ? AND name = ?').get(agentId, name);
|
|
@@ -1521,6 +1635,29 @@ function setPendingOrchestrationPolicy(entry) {
|
|
|
1521
1635
|
function clearPendingOrchestrationPolicy(conversationId) {
|
|
1522
1636
|
return getDb().prepare('DELETE FROM pending_orchestration_policy WHERE conversation_id = ?').run(conversationId).changes > 0;
|
|
1523
1637
|
}
|
|
1638
|
+
function getPolicyDetectionEventForMessage(messageId) {
|
|
1639
|
+
return getDb().prepare('SELECT * FROM policy_detection_event WHERE message_id = ?').get(messageId);
|
|
1640
|
+
}
|
|
1641
|
+
function recordPolicyDetectionEvent(entry) {
|
|
1642
|
+
const db = getDb();
|
|
1643
|
+
const existing = getPolicyDetectionEventForMessage(entry.messageId);
|
|
1644
|
+
const id = existing?.id || newId();
|
|
1645
|
+
db.prepare(`
|
|
1646
|
+
INSERT INTO policy_detection_event (
|
|
1647
|
+
id, conversation_id, message_id, detection_source, detected_by_code,
|
|
1648
|
+
signal_json, status, scope, summary_text, policy_json, created_at
|
|
1649
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
1650
|
+
ON CONFLICT(message_id) DO UPDATE SET
|
|
1651
|
+
detection_source = excluded.detection_source,
|
|
1652
|
+
detected_by_code = excluded.detected_by_code,
|
|
1653
|
+
signal_json = excluded.signal_json,
|
|
1654
|
+
status = excluded.status,
|
|
1655
|
+
scope = excluded.scope,
|
|
1656
|
+
summary_text = excluded.summary_text,
|
|
1657
|
+
policy_json = excluded.policy_json
|
|
1658
|
+
`).run(id, entry.conversationId, entry.messageId, entry.source, entry.detectedByCode ? 1 : 0, entry.matchedSignals?.length ? JSON.stringify(entry.matchedSignals) : null, entry.status, entry.scope ?? null, entry.summaryText ?? null, entry.policy ? JSON.stringify(entry.policy) : null);
|
|
1659
|
+
return getPolicyDetectionEventForMessage(entry.messageId);
|
|
1660
|
+
}
|
|
1524
1661
|
function getOrchestrationCheckpoint(conversationId) {
|
|
1525
1662
|
const row = getDb().prepare('SELECT * FROM orchestration_checkpoint WHERE conversation_id = ?').get(conversationId);
|
|
1526
1663
|
if (!row)
|
|
@@ -1620,6 +1757,19 @@ function parseJsonStatus(raw) {
|
|
|
1620
1757
|
return {};
|
|
1621
1758
|
}
|
|
1622
1759
|
}
|
|
1760
|
+
function parseJsonNumberArray(raw) {
|
|
1761
|
+
try {
|
|
1762
|
+
const parsed = JSON.parse(raw);
|
|
1763
|
+
if (!Array.isArray(parsed))
|
|
1764
|
+
return [];
|
|
1765
|
+
return parsed
|
|
1766
|
+
.map((value) => Number(value))
|
|
1767
|
+
.filter((value) => Number.isFinite(value) && value > 0);
|
|
1768
|
+
}
|
|
1769
|
+
catch {
|
|
1770
|
+
return [];
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1623
1773
|
function normalizeParticipants(input) {
|
|
1624
1774
|
const seen = new Set();
|
|
1625
1775
|
const out = [];
|
|
@@ -1642,11 +1792,27 @@ function normalizeTodoRow(row, state) {
|
|
|
1642
1792
|
conversation_id: row.conversation_id ? String(row.conversation_id) : null,
|
|
1643
1793
|
origin: row.origin ? String(row.origin) : null,
|
|
1644
1794
|
created_by_bot_id: row.created_by_bot_id ? String(row.created_by_bot_id) : null,
|
|
1795
|
+
owner_bot_id: row.owner_bot_id ? String(row.owner_bot_id) : null,
|
|
1796
|
+
owner_name: row.owner_name ? String(row.owner_name) : null,
|
|
1797
|
+
task_type: row.task_type ? String(row.task_type) : null,
|
|
1798
|
+
profile_name: row.profile_name ? String(row.profile_name) : null,
|
|
1799
|
+
next_worker_bot_id: row.next_worker_bot_id ? String(row.next_worker_bot_id) : null,
|
|
1800
|
+
next_worker_name: row.next_worker_name ? String(row.next_worker_name) : null,
|
|
1801
|
+
next_worker_role: row.next_worker_role ? String(row.next_worker_role) : null,
|
|
1645
1802
|
title: String(row.title || ''),
|
|
1646
1803
|
details: String(row.details || ''),
|
|
1804
|
+
task_prompt: row.task_prompt ? String(row.task_prompt) : null,
|
|
1805
|
+
handoff_prompt: row.handoff_prompt ? String(row.handoff_prompt) : null,
|
|
1806
|
+
output_summary: row.output_summary ? String(row.output_summary) : null,
|
|
1807
|
+
blocker_summary: row.blocker_summary ? String(row.blocker_summary) : null,
|
|
1808
|
+
blocker_checked: row.blocker_checked ? String(row.blocker_checked) : null,
|
|
1809
|
+
blocker_question: row.blocker_question ? String(row.blocker_question) : null,
|
|
1810
|
+
artifact_ids: parseJsonNumberArray(String(row.artifact_ids_json || '[]')),
|
|
1811
|
+
internal_only: Number(row.internal_only || 0),
|
|
1647
1812
|
participants: parseJsonArray(String(row.participants || '[]')),
|
|
1648
1813
|
status: parseJsonStatus(String(row.status || '{}')),
|
|
1649
1814
|
created_order: Number(row.created_order || 0),
|
|
1815
|
+
position: Number(row.created_order || 0),
|
|
1650
1816
|
state,
|
|
1651
1817
|
created_at: String(row.created_at || ''),
|
|
1652
1818
|
completed_at: row.completed_at ? String(row.completed_at) : null,
|
|
@@ -1658,6 +1824,47 @@ function normalizeTodoRow(row, state) {
|
|
|
1658
1824
|
function isUserName(name) {
|
|
1659
1825
|
return name.trim().toLowerCase() === 'user';
|
|
1660
1826
|
}
|
|
1827
|
+
function roleClassToTaskType(roleClass) {
|
|
1828
|
+
const normalized = String(roleClass || '').trim().toLowerCase();
|
|
1829
|
+
if (!normalized)
|
|
1830
|
+
return null;
|
|
1831
|
+
if (normalized === 'code' || normalized === 'coding' || normalized === 'developer')
|
|
1832
|
+
return 'coding';
|
|
1833
|
+
if (normalized === 'qa' || normalized === 'review')
|
|
1834
|
+
return 'qa';
|
|
1835
|
+
if (normalized === 'research' || normalized === 'brainstorm' || normalized === 'planning' || normalized === 'plan')
|
|
1836
|
+
return 'research';
|
|
1837
|
+
if (normalized === 'verify' || normalized === 'verification')
|
|
1838
|
+
return 'verify';
|
|
1839
|
+
if (normalized === 'deploy' || normalized === 'deployment')
|
|
1840
|
+
return 'deploy';
|
|
1841
|
+
return normalized;
|
|
1842
|
+
}
|
|
1843
|
+
function resolveAgentByName(name) {
|
|
1844
|
+
const normalized = String(name || '').trim().toLowerCase();
|
|
1845
|
+
if (!normalized)
|
|
1846
|
+
return undefined;
|
|
1847
|
+
return listAgentProfiles().find((agent) => agent.name.trim().toLowerCase() === normalized);
|
|
1848
|
+
}
|
|
1849
|
+
function inferTaskTypeForAgent(agent, requested) {
|
|
1850
|
+
const explicit = String(requested || '').trim();
|
|
1851
|
+
if (explicit)
|
|
1852
|
+
return explicit;
|
|
1853
|
+
return roleClassToTaskType(agent?.role_class) || roleClassToTaskType(agent?.role_label) || null;
|
|
1854
|
+
}
|
|
1855
|
+
function normalizeArtifactRow(row) {
|
|
1856
|
+
return {
|
|
1857
|
+
id: Number(row.id),
|
|
1858
|
+
project_id: row.project_id ? String(row.project_id) : null,
|
|
1859
|
+
conversation_id: row.conversation_id ? String(row.conversation_id) : null,
|
|
1860
|
+
artifact_type: String(row.artifact_type || 'reference'),
|
|
1861
|
+
label: row.label ? String(row.label) : null,
|
|
1862
|
+
path_or_ref: String(row.path_or_ref || ''),
|
|
1863
|
+
metadata_json: row.metadata_json ? String(row.metadata_json) : null,
|
|
1864
|
+
created_by_bot_id: row.created_by_bot_id ? String(row.created_by_bot_id) : null,
|
|
1865
|
+
created_at: String(row.created_at || ''),
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1661
1868
|
function resolveAssignedParticipant(participants, requested) {
|
|
1662
1869
|
const want = requested.trim().toLowerCase();
|
|
1663
1870
|
const found = participants.find((p) => p.trim().toLowerCase() === want);
|
|
@@ -1691,16 +1898,142 @@ function moveActiveTaskToCompletedTx(db, taskId, actor, reason) {
|
|
|
1691
1898
|
const completedAt = now();
|
|
1692
1899
|
db.prepare(`
|
|
1693
1900
|
INSERT INTO todo_completed (
|
|
1694
|
-
id, project_id, conversation_id,
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1901
|
+
id, project_id, conversation_id, origin, created_by_bot_id, owner_bot_id, owner_name,
|
|
1902
|
+
task_type, profile_name, next_worker_bot_id, next_worker_name, next_worker_role,
|
|
1903
|
+
title, details, task_prompt, handoff_prompt, output_summary, blocker_summary, blocker_checked, blocker_question, artifact_ids_json,
|
|
1904
|
+
success_criteria, internal_only, participants, status, created_order,
|
|
1905
|
+
created_at, completed_at, last_updated, version
|
|
1906
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1907
|
+
`).run(row.id, row.project_id ?? null, row.conversation_id ?? null, row.origin ?? 'user', row.created_by_bot_id ?? null, row.owner_bot_id ?? null, row.owner_name ?? null, row.task_type ?? null, row.profile_name ?? 'Programming', row.next_worker_bot_id ?? null, row.next_worker_name ?? null, row.next_worker_role ?? null, row.title, row.details, row.task_prompt ?? null, row.handoff_prompt ?? null, row.output_summary ?? null, row.blocker_summary ?? null, row.blocker_checked ?? null, row.blocker_question ?? null, row.artifact_ids_json ?? '[]', row.success_criteria ?? null, Number(row.internal_only || 0), row.participants, row.status, row.created_order, row.created_at, completedAt, completedAt, row.version);
|
|
1698
1908
|
db.prepare('DELETE FROM todo_active WHERE id = ?').run(taskId);
|
|
1699
|
-
normalizeTodoOrderTx(db, row.project_id ? String(row.project_id) : null);
|
|
1700
1909
|
logTodoAuditTx(db, taskId, 'completed', 'complete', actor, { reason });
|
|
1701
1910
|
const completed = db.prepare('SELECT * FROM todo_completed WHERE id = ?').get(taskId);
|
|
1702
1911
|
return normalizeTodoRow(completed, 'completed');
|
|
1703
1912
|
}
|
|
1913
|
+
function determineInsertPositionTx(db, projectId, insertAfterTaskId) {
|
|
1914
|
+
if (insertAfterTaskId) {
|
|
1915
|
+
const row = db.prepare(`
|
|
1916
|
+
SELECT created_order, project_id
|
|
1917
|
+
FROM todo_active
|
|
1918
|
+
WHERE id = ?
|
|
1919
|
+
UNION ALL
|
|
1920
|
+
SELECT created_order, project_id
|
|
1921
|
+
FROM todo_completed
|
|
1922
|
+
WHERE id = ?
|
|
1923
|
+
LIMIT 1
|
|
1924
|
+
`).get(insertAfterTaskId, insertAfterTaskId);
|
|
1925
|
+
if (!row)
|
|
1926
|
+
throw new Error(`Task ${insertAfterTaskId} not found`);
|
|
1927
|
+
const normalizedProjectId = projectId ?? null;
|
|
1928
|
+
if ((row.project_id ?? null) !== normalizedProjectId) {
|
|
1929
|
+
throw new Error('Inserted task must stay within the same project scope');
|
|
1930
|
+
}
|
|
1931
|
+
const targetPosition = Number(row.created_order || 0) + 1;
|
|
1932
|
+
if (normalizedProjectId) {
|
|
1933
|
+
db.prepare('UPDATE todo_active SET created_order = created_order + 1, last_updated = datetime(\'now\'), version = version + 1 WHERE project_id = ? AND created_order >= ?')
|
|
1934
|
+
.run(normalizedProjectId, targetPosition);
|
|
1935
|
+
}
|
|
1936
|
+
else {
|
|
1937
|
+
db.prepare('UPDATE todo_active SET created_order = created_order + 1, last_updated = datetime(\'now\'), version = version + 1 WHERE project_id IS NULL AND created_order >= ?')
|
|
1938
|
+
.run(targetPosition);
|
|
1939
|
+
}
|
|
1940
|
+
return targetPosition;
|
|
1941
|
+
}
|
|
1942
|
+
const maxOrder = projectId
|
|
1943
|
+
? db.prepare('SELECT COALESCE(MAX(created_order), 0) as n FROM todo_active WHERE project_id = ?').get(projectId)
|
|
1944
|
+
: db.prepare('SELECT COALESCE(MAX(created_order), 0) as n FROM todo_active WHERE project_id IS NULL').get();
|
|
1945
|
+
return Number(maxOrder.n) + 1;
|
|
1946
|
+
}
|
|
1947
|
+
function resolveOrchestratorName() {
|
|
1948
|
+
return getOrchestratorBot()?.name || null;
|
|
1949
|
+
}
|
|
1950
|
+
function resolveAllowedWorkerNamesForInsertion(currentTask) {
|
|
1951
|
+
const allowed = new Set();
|
|
1952
|
+
if (currentTask.next_worker_name)
|
|
1953
|
+
allowed.add(currentTask.next_worker_name.trim().toLowerCase());
|
|
1954
|
+
const orchestratorName = resolveOrchestratorName();
|
|
1955
|
+
if (orchestratorName)
|
|
1956
|
+
allowed.add(orchestratorName.trim().toLowerCase());
|
|
1957
|
+
const db = getDb();
|
|
1958
|
+
const previous = currentTask.project_id
|
|
1959
|
+
? db.prepare(`
|
|
1960
|
+
SELECT owner_name, created_order, internal_only
|
|
1961
|
+
FROM (
|
|
1962
|
+
SELECT owner_name, created_order, internal_only FROM todo_active WHERE project_id = ?
|
|
1963
|
+
UNION ALL
|
|
1964
|
+
SELECT owner_name, created_order, internal_only FROM todo_completed WHERE project_id = ?
|
|
1965
|
+
)
|
|
1966
|
+
WHERE created_order < ? AND COALESCE(internal_only, 0) = 0 AND owner_name IS NOT NULL
|
|
1967
|
+
ORDER BY created_order DESC
|
|
1968
|
+
LIMIT 1
|
|
1969
|
+
`).get(currentTask.project_id, currentTask.project_id, currentTask.position)
|
|
1970
|
+
: db.prepare(`
|
|
1971
|
+
SELECT owner_name, created_order, internal_only
|
|
1972
|
+
FROM (
|
|
1973
|
+
SELECT owner_name, created_order, internal_only FROM todo_active WHERE project_id IS NULL
|
|
1974
|
+
UNION ALL
|
|
1975
|
+
SELECT owner_name, created_order, internal_only FROM todo_completed WHERE project_id IS NULL
|
|
1976
|
+
)
|
|
1977
|
+
WHERE created_order < ? AND COALESCE(internal_only, 0) = 0 AND owner_name IS NOT NULL
|
|
1978
|
+
ORDER BY created_order DESC
|
|
1979
|
+
LIMIT 1
|
|
1980
|
+
`).get(currentTask.position);
|
|
1981
|
+
if (previous?.owner_name)
|
|
1982
|
+
allowed.add(previous.owner_name.trim().toLowerCase());
|
|
1983
|
+
return Array.from(allowed);
|
|
1984
|
+
}
|
|
1985
|
+
function normalizeArtifactInput(ref) {
|
|
1986
|
+
if (typeof ref === 'string') {
|
|
1987
|
+
const trimmed = ref.trim();
|
|
1988
|
+
return {
|
|
1989
|
+
pathOrRef: trimmed,
|
|
1990
|
+
artifactType: /\.[a-z0-9]+$/i.test(trimmed) || /[\\/]/.test(trimmed) ? 'file' : 'reference',
|
|
1991
|
+
label: trimmed.split(/[\\/]/).filter(Boolean).pop() || trimmed,
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
return {
|
|
1995
|
+
artifactType: String(ref.artifactType || 'reference'),
|
|
1996
|
+
label: ref.label ? String(ref.label) : undefined,
|
|
1997
|
+
pathOrRef: String(ref.pathOrRef || '').trim(),
|
|
1998
|
+
metadata: ref.metadata ?? null,
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
function resolveArtifactRowsTx(db, refs, context) {
|
|
2002
|
+
const rows = [];
|
|
2003
|
+
for (const raw of refs) {
|
|
2004
|
+
const normalized = normalizeArtifactInput(raw);
|
|
2005
|
+
if (!normalized.pathOrRef)
|
|
2006
|
+
continue;
|
|
2007
|
+
const existing = db.prepare(`
|
|
2008
|
+
SELECT * FROM todo_artifact
|
|
2009
|
+
WHERE project_id IS ? AND conversation_id IS ? AND artifact_type = ? AND path_or_ref = ?
|
|
2010
|
+
ORDER BY id DESC
|
|
2011
|
+
LIMIT 1
|
|
2012
|
+
`).get(context.projectId ?? null, context.conversationId ?? null, normalized.artifactType || 'reference', normalized.pathOrRef);
|
|
2013
|
+
if (existing) {
|
|
2014
|
+
rows.push(normalizeArtifactRow(existing));
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
const result = db.prepare(`
|
|
2018
|
+
INSERT INTO todo_artifact (
|
|
2019
|
+
project_id, conversation_id, artifact_type, label, path_or_ref, metadata_json, created_by_bot_id, created_at
|
|
2020
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
2021
|
+
`).run(context.projectId ?? null, context.conversationId ?? null, normalized.artifactType || 'reference', normalized.label ?? null, normalized.pathOrRef, normalized.metadata ? JSON.stringify(normalized.metadata) : null, context.createdByBotId ?? null, now());
|
|
2022
|
+
const created = db.prepare('SELECT * FROM todo_artifact WHERE id = ?').get(Number(result.lastInsertRowid));
|
|
2023
|
+
rows.push(normalizeArtifactRow(created));
|
|
2024
|
+
}
|
|
2025
|
+
return rows;
|
|
2026
|
+
}
|
|
2027
|
+
function loadArtifactRowsByIdsTx(db, artifactIds) {
|
|
2028
|
+
const normalized = artifactIds
|
|
2029
|
+
.map((value) => Number(value))
|
|
2030
|
+
.filter((value) => Number.isFinite(value) && value > 0);
|
|
2031
|
+
if (normalized.length === 0)
|
|
2032
|
+
return [];
|
|
2033
|
+
const placeholders = normalized.map(() => '?').join(', ');
|
|
2034
|
+
const rows = db.prepare(`SELECT * FROM todo_artifact WHERE id IN (${placeholders}) ORDER BY id ASC`).all(...normalized);
|
|
2035
|
+
return rows.map((row) => normalizeArtifactRow(row));
|
|
2036
|
+
}
|
|
1704
2037
|
function getTodoStatusMarker(projectId) {
|
|
1705
2038
|
const row = projectId
|
|
1706
2039
|
? getDb().prepare('SELECT COUNT(*) as n FROM todo_active WHERE project_id = ?').get(projectId)
|
|
@@ -1735,13 +2068,24 @@ function listAllTodoTasks(opts) {
|
|
|
1735
2068
|
};
|
|
1736
2069
|
}
|
|
1737
2070
|
function addTodoTask(params) {
|
|
1738
|
-
const db = getDb();
|
|
1739
2071
|
const title = String(params.title || '').trim();
|
|
1740
2072
|
const details = String(params.details || '').trim();
|
|
1741
2073
|
const participants = normalizeParticipants(params.participants || []);
|
|
1742
2074
|
const successCriteria = params.successCriteria?.trim() || null;
|
|
1743
2075
|
const origin = params.actor.actorType;
|
|
1744
2076
|
const createdByBotId = params.actor.actorId?.trim() || null;
|
|
2077
|
+
const ownerBotId = params.ownerBotId?.trim() || null;
|
|
2078
|
+
const ownerName = params.ownerName?.trim() || null;
|
|
2079
|
+
const taskType = params.taskType?.trim() || null;
|
|
2080
|
+
const profileName = params.profileName?.trim() || 'Programming';
|
|
2081
|
+
const nextWorkerBotId = params.nextWorkerBotId?.trim() || null;
|
|
2082
|
+
const nextWorkerName = params.nextWorkerName?.trim() || null;
|
|
2083
|
+
const nextWorkerRole = params.nextWorkerRole?.trim() || null;
|
|
2084
|
+
const taskPrompt = params.taskPrompt?.trim() || null;
|
|
2085
|
+
const handoffPrompt = params.handoffPrompt?.trim() || null;
|
|
2086
|
+
const outputSummary = params.outputSummary?.trim() || null;
|
|
2087
|
+
const artifactIdsJson = JSON.stringify((params.artifactIds || []).filter((id) => Number.isFinite(id) && id > 0));
|
|
2088
|
+
const internalOnly = params.internalOnly ? 1 : 0;
|
|
1745
2089
|
if (!title)
|
|
1746
2090
|
throw new Error('Title is required');
|
|
1747
2091
|
if (countWords(title) > TODO_TITLE_WORD_LIMIT)
|
|
@@ -1756,21 +2100,61 @@ function addTodoTask(params) {
|
|
|
1756
2100
|
if (!successCriteria)
|
|
1757
2101
|
throw new Error('Orchestrator TODOs must include success criteria');
|
|
1758
2102
|
}
|
|
1759
|
-
const
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
2103
|
+
const db = getDb();
|
|
2104
|
+
const tx = db.transaction(() => insertTodoTaskTx(db, {
|
|
2105
|
+
...params,
|
|
2106
|
+
title,
|
|
2107
|
+
details,
|
|
2108
|
+
participants,
|
|
2109
|
+
successCriteria: successCriteria || undefined,
|
|
2110
|
+
ownerBotId,
|
|
2111
|
+
ownerName,
|
|
2112
|
+
taskType,
|
|
2113
|
+
profileName,
|
|
2114
|
+
nextWorkerBotId,
|
|
2115
|
+
nextWorkerName,
|
|
2116
|
+
nextWorkerRole,
|
|
2117
|
+
taskPrompt,
|
|
2118
|
+
handoffPrompt,
|
|
2119
|
+
outputSummary,
|
|
2120
|
+
artifactIds: JSON.parse(artifactIdsJson),
|
|
2121
|
+
internalOnly: Boolean(internalOnly),
|
|
2122
|
+
}));
|
|
2123
|
+
return tx();
|
|
2124
|
+
}
|
|
2125
|
+
function insertTodoTaskTx(db, params) {
|
|
2126
|
+
const title = String(params.title || '').trim();
|
|
2127
|
+
const details = String(params.details || '').trim();
|
|
2128
|
+
const participants = normalizeParticipants(params.participants || []);
|
|
2129
|
+
const successCriteria = params.successCriteria?.trim() || null;
|
|
2130
|
+
const origin = params.actor.actorType;
|
|
2131
|
+
const createdByBotId = params.actor.actorId?.trim() || null;
|
|
2132
|
+
const ownerBotId = params.ownerBotId?.trim() || null;
|
|
2133
|
+
const ownerName = params.ownerName?.trim() || null;
|
|
2134
|
+
const taskType = params.taskType?.trim() || null;
|
|
2135
|
+
const profileName = params.profileName?.trim() || 'Programming';
|
|
2136
|
+
const nextWorkerBotId = params.nextWorkerBotId?.trim() || null;
|
|
2137
|
+
const nextWorkerName = params.nextWorkerName?.trim() || null;
|
|
2138
|
+
const nextWorkerRole = params.nextWorkerRole?.trim() || null;
|
|
2139
|
+
const taskPrompt = params.taskPrompt?.trim() || null;
|
|
2140
|
+
const handoffPrompt = params.handoffPrompt?.trim() || null;
|
|
2141
|
+
const outputSummary = params.outputSummary?.trim() || null;
|
|
2142
|
+
const artifactIdsJson = JSON.stringify((params.artifactIds || []).filter((id) => Number.isFinite(id) && id > 0));
|
|
2143
|
+
const internalOnly = params.internalOnly ? 1 : 0;
|
|
2144
|
+
{
|
|
2145
|
+
const createdOrder = determineInsertPositionTx(db, params.projectId, params.insertAfterTaskId);
|
|
1764
2146
|
const status = {};
|
|
1765
2147
|
for (const p of participants)
|
|
1766
2148
|
status[p] = false;
|
|
1767
2149
|
const result = db.prepare(`
|
|
1768
2150
|
INSERT INTO todo_active (
|
|
1769
|
-
project_id, conversation_id, origin, created_by_bot_id,
|
|
1770
|
-
|
|
2151
|
+
project_id, conversation_id, origin, created_by_bot_id, owner_bot_id, owner_name,
|
|
2152
|
+
task_type, profile_name, next_worker_bot_id, next_worker_name, next_worker_role,
|
|
2153
|
+
title, details, task_prompt, handoff_prompt, output_summary, artifact_ids_json,
|
|
2154
|
+
success_criteria, internal_only, participants, status, created_order, created_at, last_updated, version
|
|
1771
2155
|
)
|
|
1772
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)
|
|
1773
|
-
`).run(params.projectId ?? null, params.conversationId ?? null, origin, createdByBotId, title, details, JSON.stringify(participants), JSON.stringify(status), createdOrder,
|
|
2156
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)
|
|
2157
|
+
`).run(params.projectId ?? null, params.conversationId ?? null, origin, createdByBotId, ownerBotId, ownerName, taskType, profileName, nextWorkerBotId, nextWorkerName, nextWorkerRole, title, details, taskPrompt, handoffPrompt, outputSummary, artifactIdsJson, successCriteria, internalOnly, JSON.stringify(participants), JSON.stringify(status), createdOrder, now(), now());
|
|
1774
2158
|
const id = Number(result.lastInsertRowid);
|
|
1775
2159
|
logTodoAuditTx(db, id, 'active', 'add', params.actor, {
|
|
1776
2160
|
title,
|
|
@@ -1779,11 +2163,19 @@ function addTodoTask(params) {
|
|
|
1779
2163
|
conversationId: params.conversationId ?? null,
|
|
1780
2164
|
origin,
|
|
1781
2165
|
createdByBotId,
|
|
2166
|
+
ownerBotId,
|
|
2167
|
+
ownerName,
|
|
2168
|
+
taskType,
|
|
2169
|
+
profileName,
|
|
2170
|
+
nextWorkerBotId,
|
|
2171
|
+
nextWorkerName,
|
|
2172
|
+
nextWorkerRole,
|
|
2173
|
+
insertAfterTaskId: params.insertAfterTaskId ?? null,
|
|
2174
|
+
internalOnly,
|
|
1782
2175
|
});
|
|
1783
2176
|
const row = db.prepare('SELECT * FROM todo_active WHERE id = ?').get(id);
|
|
1784
2177
|
return normalizeTodoRow(row, 'active');
|
|
1785
|
-
}
|
|
1786
|
-
return tx();
|
|
2178
|
+
}
|
|
1787
2179
|
}
|
|
1788
2180
|
function editTodoTask(taskId, params) {
|
|
1789
2181
|
const db = getDb();
|
|
@@ -1919,6 +2311,289 @@ function completeTodoTaskByUser(taskId, params) {
|
|
|
1919
2311
|
});
|
|
1920
2312
|
return tx();
|
|
1921
2313
|
}
|
|
2314
|
+
function listTodoArtifactsForTask(taskId, state = 'active') {
|
|
2315
|
+
const task = getTodoTask(taskId, state);
|
|
2316
|
+
if (!task || task.artifact_ids.length === 0)
|
|
2317
|
+
return [];
|
|
2318
|
+
const placeholders = task.artifact_ids.map(() => '?').join(', ');
|
|
2319
|
+
const rows = getDb().prepare(`SELECT * FROM todo_artifact WHERE id IN (${placeholders}) ORDER BY id ASC`).all(...task.artifact_ids);
|
|
2320
|
+
return rows.map((row) => normalizeArtifactRow(row));
|
|
2321
|
+
}
|
|
2322
|
+
function completeTodoTaskByWorker(taskId, params) {
|
|
2323
|
+
if (params.actor.actorType !== 'llm' && params.actor.actorType !== 'orchestrator') {
|
|
2324
|
+
throw new Error('Only worker LLMs or orchestrator can complete workflow TODOs');
|
|
2325
|
+
}
|
|
2326
|
+
const db = getDb();
|
|
2327
|
+
const tx = db.transaction(() => {
|
|
2328
|
+
const row = db.prepare('SELECT * FROM todo_active WHERE id = ?').get(taskId);
|
|
2329
|
+
if (!row)
|
|
2330
|
+
throw new Error(`Task ${taskId} not found`);
|
|
2331
|
+
if (params.expectedVersion !== undefined && Number(row.version) !== Number(params.expectedVersion)) {
|
|
2332
|
+
throw new Error('Conflict: task version changed');
|
|
2333
|
+
}
|
|
2334
|
+
if (params.expectedLastUpdated !== undefined && String(row.last_updated) !== String(params.expectedLastUpdated)) {
|
|
2335
|
+
throw new Error('Conflict: task was updated by another actor');
|
|
2336
|
+
}
|
|
2337
|
+
const currentTask = normalizeTodoRow(row, 'active');
|
|
2338
|
+
const actorName = String(params.actor.actorId || '').trim();
|
|
2339
|
+
const actorBot = resolveAgentByName(actorName);
|
|
2340
|
+
const isAssignedWorker = Boolean(actorName &&
|
|
2341
|
+
currentTask.owner_name &&
|
|
2342
|
+
actorName.toLowerCase() === currentTask.owner_name.toLowerCase());
|
|
2343
|
+
const isOrchestratorActor = params.actor.actorType === 'orchestrator';
|
|
2344
|
+
if (!isAssignedWorker && !isOrchestratorActor) {
|
|
2345
|
+
throw new Error(`Actor "${actorName || 'unknown'}" is not the owner of task ${taskId}`);
|
|
2346
|
+
}
|
|
2347
|
+
const existingArtifactRows = loadArtifactRowsByIdsTx(db, currentTask.artifact_ids);
|
|
2348
|
+
const newArtifactRows = resolveArtifactRowsTx(db, params.artifactRefs || [], {
|
|
2349
|
+
projectId: currentTask.project_id,
|
|
2350
|
+
conversationId: currentTask.conversation_id,
|
|
2351
|
+
createdByBotId: actorBot?.id || currentTask.owner_bot_id || currentTask.created_by_bot_id,
|
|
2352
|
+
});
|
|
2353
|
+
const artifactRowsById = new Map();
|
|
2354
|
+
for (const item of [...existingArtifactRows, ...newArtifactRows])
|
|
2355
|
+
artifactRowsById.set(item.id, item);
|
|
2356
|
+
const artifactRows = Array.from(artifactRowsById.values()).sort((a, b) => a.id - b.id);
|
|
2357
|
+
const artifactIds = artifactRows.map((item) => item.id);
|
|
2358
|
+
const outputSummary = params.outputSummary?.trim() || currentTask.output_summary || null;
|
|
2359
|
+
const handoffPrompt = params.handoffPrompt?.trim() || currentTask.handoff_prompt || null;
|
|
2360
|
+
db.prepare(`
|
|
2361
|
+
UPDATE todo_active
|
|
2362
|
+
SET output_summary = ?,
|
|
2363
|
+
handoff_prompt = ?,
|
|
2364
|
+
artifact_ids_json = ?,
|
|
2365
|
+
last_updated = ?,
|
|
2366
|
+
version = version + 1
|
|
2367
|
+
WHERE id = ?
|
|
2368
|
+
`).run(outputSummary, handoffPrompt, JSON.stringify(artifactIds), now(), taskId);
|
|
2369
|
+
logTodoAuditTx(db, taskId, 'active', 'worker_complete', params.actor, {
|
|
2370
|
+
outputSummary,
|
|
2371
|
+
artifactIds,
|
|
2372
|
+
inserted: Boolean(params.insertTask),
|
|
2373
|
+
});
|
|
2374
|
+
const completedTask = moveActiveTaskToCompletedTx(db, taskId, params.actor, 'all_llms_complete');
|
|
2375
|
+
let insertedTask;
|
|
2376
|
+
let handedOffTask;
|
|
2377
|
+
let returnedToOrchestrator = false;
|
|
2378
|
+
if (params.insertTask) {
|
|
2379
|
+
const allowedTargets = resolveAllowedWorkerNamesForInsertion(completedTask);
|
|
2380
|
+
const requestedWorkerName = String(params.insertTask.nextWorker || '').trim();
|
|
2381
|
+
if (!requestedWorkerName) {
|
|
2382
|
+
throw new Error('Inserted worker task requires a next worker');
|
|
2383
|
+
}
|
|
2384
|
+
if (!allowedTargets.includes(requestedWorkerName.toLowerCase())) {
|
|
2385
|
+
throw new Error(`Next worker "${requestedWorkerName}" is not allowed from this task`);
|
|
2386
|
+
}
|
|
2387
|
+
const targetWorker = resolveAgentByName(requestedWorkerName);
|
|
2388
|
+
const orchestratorName = resolveOrchestratorName();
|
|
2389
|
+
if (!targetWorker && (!orchestratorName || requestedWorkerName.toLowerCase() !== orchestratorName.toLowerCase())) {
|
|
2390
|
+
throw new Error(`Unknown next worker "${requestedWorkerName}"`);
|
|
2391
|
+
}
|
|
2392
|
+
if (orchestratorName && requestedWorkerName.toLowerCase() === orchestratorName.toLowerCase()) {
|
|
2393
|
+
returnedToOrchestrator = true;
|
|
2394
|
+
logTodoAuditTx(db, taskId, 'completed', 'return_to_orchestrator', params.actor, {
|
|
2395
|
+
handoffPrompt: params.insertTask.prompt,
|
|
2396
|
+
title: params.insertTask.title,
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
else {
|
|
2400
|
+
const participants = targetWorker?.name ? [targetWorker.name] : [requestedWorkerName];
|
|
2401
|
+
insertedTask = insertTodoTaskTx(db, {
|
|
2402
|
+
projectId: completedTask.project_id,
|
|
2403
|
+
conversationId: completedTask.conversation_id,
|
|
2404
|
+
title: params.insertTask.title,
|
|
2405
|
+
details: params.insertTask.details || params.insertTask.prompt,
|
|
2406
|
+
participants,
|
|
2407
|
+
successCriteria: params.insertTask.successCriteria || undefined,
|
|
2408
|
+
ownerBotId: targetWorker?.id || null,
|
|
2409
|
+
ownerName: targetWorker?.name || requestedWorkerName,
|
|
2410
|
+
taskType: inferTaskTypeForAgent(targetWorker, params.insertTask.taskType),
|
|
2411
|
+
profileName: completedTask.profile_name || 'Programming',
|
|
2412
|
+
nextWorkerBotId: currentTask.owner_bot_id,
|
|
2413
|
+
nextWorkerName: currentTask.owner_name,
|
|
2414
|
+
nextWorkerRole: actorBot?.role_label || actorBot?.role_class || null,
|
|
2415
|
+
taskPrompt: params.insertTask.prompt,
|
|
2416
|
+
artifactIds,
|
|
2417
|
+
insertAfterTaskId: completedTask.id,
|
|
2418
|
+
actor: params.actor,
|
|
2419
|
+
});
|
|
2420
|
+
logTodoAuditTx(db, insertedTask.id, 'active', 'worker_insert', params.actor, {
|
|
2421
|
+
insertedAfterTaskId: completedTask.id,
|
|
2422
|
+
requestedWorkerName,
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
else {
|
|
2427
|
+
const nextWorkerName = completedTask.next_worker_name?.trim();
|
|
2428
|
+
if (nextWorkerName) {
|
|
2429
|
+
const nextRow = completedTask.next_worker_bot_id
|
|
2430
|
+
? db.prepare(`
|
|
2431
|
+
SELECT * FROM todo_active
|
|
2432
|
+
WHERE conversation_id = ?
|
|
2433
|
+
AND id <> ?
|
|
2434
|
+
AND owner_bot_id = ?
|
|
2435
|
+
AND created_order > ?
|
|
2436
|
+
AND COALESCE(internal_only, 0) = 0
|
|
2437
|
+
ORDER BY created_order ASC, id ASC
|
|
2438
|
+
LIMIT 1
|
|
2439
|
+
`).get(completedTask.conversation_id, completedTask.id, completedTask.next_worker_bot_id, completedTask.created_order)
|
|
2440
|
+
: db.prepare(`
|
|
2441
|
+
SELECT * FROM todo_active
|
|
2442
|
+
WHERE conversation_id = ?
|
|
2443
|
+
AND id <> ?
|
|
2444
|
+
AND LOWER(COALESCE(owner_name, '')) = LOWER(?)
|
|
2445
|
+
AND created_order > ?
|
|
2446
|
+
AND COALESCE(internal_only, 0) = 0
|
|
2447
|
+
ORDER BY created_order ASC, id ASC
|
|
2448
|
+
LIMIT 1
|
|
2449
|
+
`).get(completedTask.conversation_id, completedTask.id, nextWorkerName, completedTask.created_order);
|
|
2450
|
+
if (nextRow) {
|
|
2451
|
+
const nextTask = normalizeTodoRow(nextRow, 'active');
|
|
2452
|
+
const handoffText = handoffPrompt?.trim()
|
|
2453
|
+
|| outputSummary?.trim()
|
|
2454
|
+
|| `Continue from the completed work summary: ${completedTask.title}`;
|
|
2455
|
+
const existingPrompt = nextTask.task_prompt?.trim() || nextTask.details || nextTask.title;
|
|
2456
|
+
const mergedArtifactIds = Array.from(new Set([...(nextTask.artifact_ids || []), ...artifactIds]));
|
|
2457
|
+
const nextPrompt = [
|
|
2458
|
+
'PREVIOUS WORKER HANDOFF',
|
|
2459
|
+
handoffText,
|
|
2460
|
+
'',
|
|
2461
|
+
'ORIGINAL ASSIGNED TODO',
|
|
2462
|
+
existingPrompt,
|
|
2463
|
+
].join('\n');
|
|
2464
|
+
db.prepare(`
|
|
2465
|
+
UPDATE todo_active
|
|
2466
|
+
SET task_prompt = ?,
|
|
2467
|
+
artifact_ids_json = ?,
|
|
2468
|
+
last_updated = ?,
|
|
2469
|
+
version = version + 1
|
|
2470
|
+
WHERE id = ?
|
|
2471
|
+
`).run(nextPrompt, JSON.stringify(mergedArtifactIds), now(), nextTask.id);
|
|
2472
|
+
handedOffTask = normalizeTodoRow(db.prepare('SELECT * FROM todo_active WHERE id = ?').get(nextTask.id), 'active');
|
|
2473
|
+
logTodoAuditTx(db, nextTask.id, 'active', 'edit', params.actor, {
|
|
2474
|
+
previousTaskId: completedTask.id,
|
|
2475
|
+
receivedHandoff: true,
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
else {
|
|
2479
|
+
returnedToOrchestrator = true;
|
|
2480
|
+
logTodoAuditTx(db, taskId, 'completed', 'return_to_orchestrator', params.actor, {
|
|
2481
|
+
handoffPrompt,
|
|
2482
|
+
missingQueuedNextWorker: nextWorkerName,
|
|
2483
|
+
});
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
else {
|
|
2487
|
+
returnedToOrchestrator = true;
|
|
2488
|
+
logTodoAuditTx(db, taskId, 'completed', 'return_to_orchestrator', params.actor, {
|
|
2489
|
+
handoffPrompt,
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
return {
|
|
2494
|
+
completedTask,
|
|
2495
|
+
insertedTask,
|
|
2496
|
+
handedOffTask,
|
|
2497
|
+
artifacts: artifactRows,
|
|
2498
|
+
returnedToOrchestrator,
|
|
2499
|
+
};
|
|
2500
|
+
});
|
|
2501
|
+
return tx();
|
|
2502
|
+
}
|
|
2503
|
+
function blockTodoTaskByWorker(taskId, params) {
|
|
2504
|
+
if (params.actor.actorType !== 'llm' && params.actor.actorType !== 'orchestrator') {
|
|
2505
|
+
throw new Error('Only worker LLMs or orchestrator can block workflow TODOs');
|
|
2506
|
+
}
|
|
2507
|
+
const blockerSummary = String(params.blockerSummary || '').trim();
|
|
2508
|
+
const checkedContext = String(params.checkedContext || '').trim();
|
|
2509
|
+
const userQuestion = String(params.userQuestion || '').trim();
|
|
2510
|
+
if (!blockerSummary)
|
|
2511
|
+
throw new Error('Blocked worker task requires blockerSummary');
|
|
2512
|
+
if (!checkedContext)
|
|
2513
|
+
throw new Error('Blocked worker task requires checkedContext');
|
|
2514
|
+
if (!userQuestion)
|
|
2515
|
+
throw new Error('Blocked worker task requires userQuestion');
|
|
2516
|
+
const db = getDb();
|
|
2517
|
+
const tx = db.transaction(() => {
|
|
2518
|
+
const row = db.prepare('SELECT * FROM todo_active WHERE id = ?').get(taskId);
|
|
2519
|
+
if (!row)
|
|
2520
|
+
throw new Error(`Task ${taskId} not found`);
|
|
2521
|
+
if (params.expectedVersion !== undefined && Number(row.version) !== Number(params.expectedVersion)) {
|
|
2522
|
+
throw new Error('Conflict: task version changed');
|
|
2523
|
+
}
|
|
2524
|
+
if (params.expectedLastUpdated !== undefined && String(row.last_updated) !== String(params.expectedLastUpdated)) {
|
|
2525
|
+
throw new Error('Conflict: task was updated by another actor');
|
|
2526
|
+
}
|
|
2527
|
+
const currentTask = normalizeTodoRow(row, 'active');
|
|
2528
|
+
const actorName = String(params.actor.actorId || '').trim();
|
|
2529
|
+
const actorBot = resolveAgentByName(actorName);
|
|
2530
|
+
const isAssignedWorker = Boolean(actorName &&
|
|
2531
|
+
currentTask.owner_name &&
|
|
2532
|
+
actorName.toLowerCase() === currentTask.owner_name.toLowerCase());
|
|
2533
|
+
const isOrchestratorActor = params.actor.actorType === 'orchestrator';
|
|
2534
|
+
if (!isAssignedWorker && !isOrchestratorActor) {
|
|
2535
|
+
throw new Error(`Actor "${actorName || 'unknown'}" is not the owner of task ${taskId}`);
|
|
2536
|
+
}
|
|
2537
|
+
const existingArtifactRows = loadArtifactRowsByIdsTx(db, currentTask.artifact_ids);
|
|
2538
|
+
const newArtifactRows = resolveArtifactRowsTx(db, params.artifactRefs || [], {
|
|
2539
|
+
projectId: currentTask.project_id,
|
|
2540
|
+
conversationId: currentTask.conversation_id,
|
|
2541
|
+
createdByBotId: actorBot?.id || currentTask.owner_bot_id || currentTask.created_by_bot_id,
|
|
2542
|
+
});
|
|
2543
|
+
const artifactRowsById = new Map();
|
|
2544
|
+
for (const item of [...existingArtifactRows, ...newArtifactRows])
|
|
2545
|
+
artifactRowsById.set(item.id, item);
|
|
2546
|
+
const artifactRows = Array.from(artifactRowsById.values()).sort((a, b) => a.id - b.id);
|
|
2547
|
+
const artifactIds = artifactRows.map((item) => item.id);
|
|
2548
|
+
db.prepare(`
|
|
2549
|
+
UPDATE todo_active
|
|
2550
|
+
SET blocker_summary = ?,
|
|
2551
|
+
blocker_checked = ?,
|
|
2552
|
+
blocker_question = ?,
|
|
2553
|
+
artifact_ids_json = ?,
|
|
2554
|
+
last_updated = ?,
|
|
2555
|
+
version = version + 1
|
|
2556
|
+
WHERE id = ?
|
|
2557
|
+
`).run(blockerSummary, checkedContext, userQuestion, JSON.stringify(artifactIds), now(), taskId);
|
|
2558
|
+
logTodoAuditTx(db, taskId, 'active', 'worker_blocked', params.actor, {
|
|
2559
|
+
blockerSummary,
|
|
2560
|
+
checkedContext,
|
|
2561
|
+
userQuestion,
|
|
2562
|
+
artifactIds,
|
|
2563
|
+
});
|
|
2564
|
+
logTodoAuditTx(db, taskId, 'active', 'return_to_orchestrator', params.actor, {
|
|
2565
|
+
blocked: true,
|
|
2566
|
+
blockerSummary,
|
|
2567
|
+
userQuestion,
|
|
2568
|
+
});
|
|
2569
|
+
const blockedTask = normalizeTodoRow(db.prepare('SELECT * FROM todo_active WHERE id = ?').get(taskId), 'active');
|
|
2570
|
+
const orchestratorBot = getOrchestratorBot();
|
|
2571
|
+
const ownerRole = String(actorBot?.role_label || actorBot?.role_class || currentTask.task_type || 'general').trim() || 'general';
|
|
2572
|
+
const effectivePolicy = getEffectiveOrchestrationPolicy(currentTask.project_id || undefined);
|
|
2573
|
+
const orchestratorPrompt = (0, orchestrator_blocked_prompt_1.buildBlockedWorkerOrchestratorPrompt)({
|
|
2574
|
+
orchestratorName: orchestratorBot?.name || 'Orchestrator',
|
|
2575
|
+
workerName: blockedTask.owner_name || actorName || 'Worker',
|
|
2576
|
+
workerRole: ownerRole,
|
|
2577
|
+
taskTitle: blockedTask.title,
|
|
2578
|
+
taskPrompt: blockedTask.task_prompt || blockedTask.details || '(not set)',
|
|
2579
|
+
projectId: blockedTask.project_id,
|
|
2580
|
+
projectName: blockedTask.project_id ? getProject(blockedTask.project_id)?.name || null : null,
|
|
2581
|
+
workspacePath: blockedTask.project_id ? getProject(blockedTask.project_id)?.folder || null : null,
|
|
2582
|
+
effectivePolicy,
|
|
2583
|
+
artifactRefs: artifactRows.map((item) => item.path_or_ref),
|
|
2584
|
+
blockerSummary,
|
|
2585
|
+
checkedContext,
|
|
2586
|
+
userQuestion,
|
|
2587
|
+
});
|
|
2588
|
+
return {
|
|
2589
|
+
blockedTask,
|
|
2590
|
+
artifacts: artifactRows,
|
|
2591
|
+
returnedToOrchestrator: true,
|
|
2592
|
+
orchestratorPrompt,
|
|
2593
|
+
};
|
|
2594
|
+
});
|
|
2595
|
+
return tx();
|
|
2596
|
+
}
|
|
1922
2597
|
function deleteTodoTask(taskId, params) {
|
|
1923
2598
|
if (params.actor.actorType !== 'user' && params.actor.actorType !== 'orchestrator')
|
|
1924
2599
|
throw new Error('Only user or orchestrator can delete tasks');
|
|
@@ -1999,6 +2674,16 @@ function getLatestActiveTodoForConversation(conversationId) {
|
|
|
1999
2674
|
return undefined;
|
|
2000
2675
|
return normalizeTodoRow(row, 'active');
|
|
2001
2676
|
}
|
|
2677
|
+
function getNextActiveTodoForConversation(conversationId) {
|
|
2678
|
+
const row = getDb().prepare('SELECT * FROM todo_active WHERE conversation_id = ? AND COALESCE(internal_only, 0) = 0 ORDER BY created_order ASC, id ASC LIMIT 1').get(conversationId);
|
|
2679
|
+
if (!row)
|
|
2680
|
+
return undefined;
|
|
2681
|
+
return normalizeTodoRow(row, 'active');
|
|
2682
|
+
}
|
|
2683
|
+
function listCompletedTodoTasksForConversation(conversationId) {
|
|
2684
|
+
const rows = getDb().prepare('SELECT * FROM todo_completed WHERE conversation_id = ? AND COALESCE(internal_only, 0) = 0 ORDER BY created_order ASC, id ASC').all(conversationId);
|
|
2685
|
+
return rows.map((row) => normalizeTodoRow(row, 'completed'));
|
|
2686
|
+
}
|
|
2002
2687
|
exports.UNASSIGNED_PROJECT_NAME = 'unassigned';
|
|
2003
2688
|
function createProject(p) {
|
|
2004
2689
|
const db = getDb();
|
|
@@ -2008,6 +2693,14 @@ function createProject(p) {
|
|
|
2008
2693
|
.run(id, p.name, p.description ?? null, p.folder ?? null, p.operatingPrompt ?? null, ts, ts);
|
|
2009
2694
|
return db.prepare('SELECT * FROM project WHERE id = ?').get(id);
|
|
2010
2695
|
}
|
|
2696
|
+
function upsertProject(project) {
|
|
2697
|
+
const db = getDb();
|
|
2698
|
+
db.prepare(`
|
|
2699
|
+
INSERT INTO project (id, name, folder, description, created_at, updated_at, archived, sort_order)
|
|
2700
|
+
VALUES (?, ?, ?, ?, datetime('now'), datetime('now'), 0, 0)
|
|
2701
|
+
ON CONFLICT(id) DO UPDATE SET name=excluded.name, folder=excluded.folder, description=excluded.description, updated_at=datetime('now')
|
|
2702
|
+
`).run(project.id, project.name, project.folder || null, project.description || null);
|
|
2703
|
+
}
|
|
2011
2704
|
function getProject(id) {
|
|
2012
2705
|
const db = getDb();
|
|
2013
2706
|
const row = db.prepare('SELECT * FROM project WHERE id = ?').get(id);
|
|
@@ -2088,7 +2781,7 @@ function getProjectOverview(projectId) {
|
|
|
2088
2781
|
label: connection.label || null,
|
|
2089
2782
|
status: connection.status,
|
|
2090
2783
|
defaultModel: connection.default_model || null,
|
|
2091
|
-
hasSecret:
|
|
2784
|
+
hasSecret: connection.access_mode === 'cli' || Boolean(connection.api_key_enc),
|
|
2092
2785
|
}));
|
|
2093
2786
|
const userPolicy = getUserOrchestrationPolicy() || null;
|
|
2094
2787
|
const projectPolicyOverride = getProjectOrchestrationPolicyOverride(projectId) || null;
|