@lota-sdk/core 0.2.3 → 0.3.1
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/infrastructure/schema/00_identity.surql +2 -2
- package/infrastructure/schema/00_thread.surql +73 -0
- package/infrastructure/schema/02_execution_plan.surql +10 -11
- package/infrastructure/schema/04_runtime_bootstrap.surql +1 -0
- package/infrastructure/schema/10_autonomous_job.surql +3 -3
- package/package.json +2 -2
- package/src/ai/definitions.ts +1 -1
- package/src/config/agent-defaults.ts +5 -5
- package/src/config/index.ts +1 -1
- package/src/config/thread-defaults.ts +72 -0
- package/src/create-runtime.ts +90 -94
- package/src/db/record-id.ts +21 -21
- package/src/db/service.ts +44 -40
- package/src/db/tables.ts +3 -3
- package/src/db/{workstream-message-row.ts → thread-message-row.ts} +3 -3
- package/src/queues/context-compaction.queue.ts +6 -6
- package/src/queues/plan-agent-heartbeat.queue.ts +3 -3
- package/src/queues/post-chat-memory.queue.ts +1 -1
- package/src/queues/title-generation.queue.ts +10 -13
- package/src/redis/index.ts +1 -1
- package/src/redis/stream-context.ts +1 -1
- package/src/runtime/agent-identity-overrides.ts +1 -1
- package/src/runtime/agent-runtime-policy.ts +19 -21
- package/src/runtime/chat-request-routing.ts +1 -1
- package/src/runtime/context-compaction-constants.ts +1 -1
- package/src/runtime/context-compaction.ts +1 -1
- package/src/runtime/execution-plan.ts +1 -1
- package/src/runtime/index.ts +1 -1
- package/src/runtime/memory-digest-policy.ts +1 -1
- package/src/runtime/plugin-types.ts +1 -1
- package/src/runtime/post-turn-side-effects.ts +35 -35
- package/src/runtime/runtime-config.ts +24 -21
- package/src/runtime/runtime-extensions.ts +11 -11
- package/src/runtime/social-chat-agent-runner.ts +3 -3
- package/src/runtime/social-chat-history.ts +1 -1
- package/src/runtime/social-chat.ts +6 -6
- package/src/runtime/team-consultation-orchestrator.ts +1 -1
- package/src/runtime/{workstream-chat-helpers.ts → thread-chat-helpers.ts} +7 -7
- package/src/runtime/{workstream-plan-turn.ts → thread-plan-turn.ts} +11 -17
- package/src/runtime/{workstream-turn-context.ts → thread-turn-context.ts} +10 -10
- package/src/services/agent-activity.service.ts +39 -44
- package/src/services/agent-executor.service.ts +17 -19
- package/src/services/attachment.service.ts +4 -8
- package/src/services/autonomous-job.service.ts +29 -28
- package/src/services/context-compaction.service.ts +19 -29
- package/src/services/execution-plan.service.ts +58 -70
- package/src/services/global-orchestrator.service.ts +5 -5
- package/src/services/index.ts +6 -6
- package/src/services/memory.service.ts +1 -1
- package/src/services/monitoring-window.service.ts +2 -2
- package/src/services/mutating-approval.service.ts +7 -10
- package/src/services/node-workspace.service.ts +8 -7
- package/src/services/notification.service.ts +1 -1
- package/src/services/organization.service.ts +9 -9
- package/src/services/ownership-dispatcher.service.ts +13 -19
- package/src/services/plan-agent-heartbeat.service.ts +13 -13
- package/src/services/plan-agent-query.service.ts +7 -7
- package/src/services/plan-artifact.service.ts +1 -2
- package/src/services/plan-coordination.service.ts +4 -4
- package/src/services/plan-cycle.service.ts +7 -7
- package/src/services/plan-deadline.service.ts +4 -4
- package/src/services/plan-event-delivery.service.ts +8 -12
- package/src/services/plan-executor.service.ts +25 -39
- package/src/services/plan-run-data.ts +27 -8
- package/src/services/plan-run.service.ts +7 -9
- package/src/services/plan-scheduler.service.ts +4 -4
- package/src/services/plan-template.service.ts +2 -2
- package/src/services/plan-validator.service.ts +0 -11
- package/src/services/plugin-executor.service.ts +1 -1
- package/src/services/queue-job.service.ts +1 -1
- package/src/services/recent-activity-title.service.ts +1 -1
- package/src/services/recent-activity.service.ts +4 -4
- package/src/services/system-executor.service.ts +2 -2
- package/src/services/{workstream-message.service.ts → thread-message.service.ts} +72 -76
- package/src/services/thread-plan-registry.service.ts +22 -0
- package/src/services/thread-title.service.ts +39 -0
- package/src/services/{workstream-turn-preparation.service.ts → thread-turn-preparation.service.ts} +148 -171
- package/src/services/{workstream-turn.ts → thread-turn.ts} +27 -31
- package/src/services/thread.service.ts +853 -0
- package/src/services/thread.types.ts +17 -0
- package/src/storage/attachment-storage.service.ts +4 -4
- package/src/system-agents/index.ts +1 -1
- package/src/system-agents/memory.agent.ts +1 -1
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
- package/src/system-agents/regular-chat-memory-digest.agent.ts +1 -1
- package/src/system-agents/researcher.agent.ts +3 -3
- package/src/system-agents/{workstream-router.agent.ts → thread-router.agent.ts} +68 -135
- package/src/system-agents/title-generator.agent.ts +8 -8
- package/src/tools/execution-plan.tool.ts +39 -40
- package/src/tools/memory-block.tool.ts +4 -4
- package/src/tools/research-topic.tool.ts +1 -0
- package/src/tools/search-web.tool.ts +1 -1
- package/src/tools/search.tool.ts +4 -4
- package/src/tools/team-think.tool.ts +9 -9
- package/src/utils/async.ts +6 -7
- package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
- package/src/workers/regular-chat-memory-digest.runner.ts +43 -43
- package/src/workers/skill-extraction.runner.ts +9 -13
- package/src/workers/utils/{workstream-message-query.ts → thread-message-query.ts} +21 -21
- package/infrastructure/schema/00_workstream.surql +0 -64
- package/src/config/workstream-defaults.ts +0 -72
- package/src/services/workstream-plan-registry.service.ts +0 -22
- package/src/services/workstream-title.service.ts +0 -42
- package/src/services/workstream.service.ts +0 -803
- package/src/services/workstream.types.ts +0 -17
- /package/src/services/{workstream-constants.ts → thread-constants.ts} +0 -0
|
@@ -9,8 +9,8 @@ import { resolvePlanNodeExecutionVisibility } from '../runtime/execution-plan-vi
|
|
|
9
9
|
import { getRuntimeAdapters } from '../runtime/runtime-extensions'
|
|
10
10
|
import type { LotaRuntimePlanEventEnvelope } from '../runtime/runtime-extensions'
|
|
11
11
|
import { planRunService } from './plan-run.service'
|
|
12
|
+
import { ThreadSchema } from './thread.types'
|
|
12
13
|
import { userService } from './user.service'
|
|
13
|
-
import { WorkstreamSchema } from './workstream.types'
|
|
14
14
|
|
|
15
15
|
const PLAN_EVENT_DELIVERED_TTL_MS = 7 * 24 * 60 * 60 * 1000
|
|
16
16
|
|
|
@@ -41,24 +41,20 @@ class PlanEventDeliveryService {
|
|
|
41
41
|
private async deliverEvent(rawEvent: PlanEventRecord): Promise<void> {
|
|
42
42
|
const event = PlanEventSchema.parse(rawEvent)
|
|
43
43
|
const run = await planRunService.getRunById(event.runId)
|
|
44
|
-
const [spec, nodeSpecs, nodeRuns,
|
|
44
|
+
const [spec, nodeSpecs, nodeRuns, thread] = await Promise.all([
|
|
45
45
|
planRunService.getPlanSpecById(run.planSpecId),
|
|
46
46
|
planRunService.listNodeSpecs(run.planSpecId),
|
|
47
47
|
planRunService.listNodeRuns(run.id),
|
|
48
|
-
databaseService.findOne(
|
|
49
|
-
TABLES.WORKSTREAM,
|
|
50
|
-
{ id: ensureRecordId(run.workstreamId, TABLES.WORKSTREAM) },
|
|
51
|
-
WorkstreamSchema,
|
|
52
|
-
),
|
|
48
|
+
databaseService.findOne(TABLES.THREAD, { id: ensureRecordId(run.threadId, TABLES.THREAD) }, ThreadSchema),
|
|
53
49
|
])
|
|
54
50
|
const nodeSpecsById = new Map(nodeSpecs.map((nodeSpec) => [nodeSpec.nodeId, nodeSpec]))
|
|
55
51
|
const nodeRunsById = new Map(nodeRuns.map((nodeRun) => [nodeRun.nodeId, nodeRun]))
|
|
56
52
|
const organizationId = recordIdToString(run.organizationId, TABLES.ORGANIZATION)
|
|
57
|
-
const
|
|
53
|
+
const threadId = recordIdToString(run.threadId, TABLES.THREAD)
|
|
58
54
|
const runIdString = recordIdToString(run.id, TABLES.PLAN_RUN)
|
|
59
55
|
const planSpecId = recordIdToString(spec.id, TABLES.PLAN_SPEC)
|
|
60
|
-
const userId =
|
|
61
|
-
? recordIdToString(ensureRecordId(
|
|
56
|
+
const userId = thread?.userId
|
|
57
|
+
? recordIdToString(ensureRecordId(thread.userId, TABLES.USER), TABLES.USER)
|
|
62
58
|
: undefined
|
|
63
59
|
const userName =
|
|
64
60
|
userId === undefined
|
|
@@ -75,7 +71,7 @@ class PlanEventDeliveryService {
|
|
|
75
71
|
...(event.nodeId ? { nodeSpec: nodeSpecsById.get(event.nodeId) } : {}),
|
|
76
72
|
...(event.nodeId ? { nodeRun: nodeRunsById.get(event.nodeId) } : {}),
|
|
77
73
|
organizationId,
|
|
78
|
-
|
|
74
|
+
threadId,
|
|
79
75
|
runId: runIdString,
|
|
80
76
|
planSpecId,
|
|
81
77
|
...(userId ? { userId } : {}),
|
|
@@ -123,7 +119,7 @@ class PlanEventDeliveryService {
|
|
|
123
119
|
const { enqueuePlanAgentHeartbeatWake } = await import('../queues/plan-agent-heartbeat.queue')
|
|
124
120
|
await enqueuePlanAgentHeartbeatWake({
|
|
125
121
|
organizationId: envelope.organizationId,
|
|
126
|
-
|
|
122
|
+
threadId: envelope.threadId,
|
|
127
123
|
runId: envelope.runId,
|
|
128
124
|
nodeId: wakeTarget.nodeId,
|
|
129
125
|
agentId: wakeTarget.agentId,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
STRUCTURAL_NODE_TYPES as STRUCTURAL_NODE_TYPE_VALUES,
|
|
22
22
|
PlanValidationIssueSchema,
|
|
23
23
|
} from '@lota-sdk/shared'
|
|
24
|
-
import { RecordId } from 'surrealdb'
|
|
24
|
+
import { RecordId, StringRecordId } from 'surrealdb'
|
|
25
25
|
|
|
26
26
|
import { aiLogger } from '../config/logger'
|
|
27
27
|
import type { RecordIdInput } from '../db/record-id'
|
|
@@ -92,20 +92,15 @@ function parseLiteralValue(raw: string): unknown {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
function buildArtifactContext(
|
|
96
|
-
artifacts: Array<{ name: string; kind: string; pointer: string; schemaRef?: string; payload?: unknown }>,
|
|
97
|
-
) {
|
|
95
|
+
function buildArtifactContext(artifacts: Array<{ name: string; kind: string; payload?: unknown }>) {
|
|
98
96
|
return Object.fromEntries(
|
|
99
|
-
artifacts.map((artifact) => [
|
|
100
|
-
artifact.name,
|
|
101
|
-
{ kind: artifact.kind, pointer: artifact.pointer, schemaRef: artifact.schemaRef, payload: artifact.payload },
|
|
102
|
-
]),
|
|
97
|
+
artifacts.map((artifact) => [artifact.name, { kind: artifact.kind, payload: artifact.payload }]),
|
|
103
98
|
)
|
|
104
99
|
}
|
|
105
100
|
|
|
106
101
|
function buildNodeContext(params: {
|
|
107
102
|
nodeRun: PlanNodeRunRecord | undefined
|
|
108
|
-
artifacts: Array<{ name: string; kind: string;
|
|
103
|
+
artifacts: Array<{ name: string; kind: string; payload?: unknown }>
|
|
109
104
|
}) {
|
|
110
105
|
return {
|
|
111
106
|
input: params.nodeRun?.resolvedInput ?? {},
|
|
@@ -159,7 +154,6 @@ type PlanNodeRunUpdate = Omit<
|
|
|
159
154
|
| 'resolvedInput'
|
|
160
155
|
| 'latestStructuredOutput'
|
|
161
156
|
| 'latestNotes'
|
|
162
|
-
| 'handoffContext'
|
|
163
157
|
| 'latestAttemptId'
|
|
164
158
|
| 'scheduledAt'
|
|
165
159
|
| 'readyAt'
|
|
@@ -171,7 +165,6 @@ type PlanNodeRunUpdate = Omit<
|
|
|
171
165
|
resolvedInput?: Record<string, unknown> | null
|
|
172
166
|
latestStructuredOutput?: Record<string, unknown> | null
|
|
173
167
|
latestNotes?: string | null
|
|
174
|
-
handoffContext?: Record<string, unknown> | null
|
|
175
168
|
latestAttemptId?: RecordIdInput | null
|
|
176
169
|
scheduledAt?: string | Date | null
|
|
177
170
|
readyAt?: string | Date | null
|
|
@@ -208,13 +201,6 @@ function toNodeRunData(nodeRun: PlanNodeRunRecord, patch: PlanNodeRunUpdate) {
|
|
|
208
201
|
: nodeRun.latestNotes
|
|
209
202
|
? { latestNotes: nodeRun.latestNotes }
|
|
210
203
|
: {}),
|
|
211
|
-
...(patch.handoffContext === null
|
|
212
|
-
? {}
|
|
213
|
-
: patch.handoffContext !== undefined
|
|
214
|
-
? { handoffContext: patch.handoffContext }
|
|
215
|
-
: nodeRun.handoffContext
|
|
216
|
-
? { handoffContext: nodeRun.handoffContext }
|
|
217
|
-
: {}),
|
|
218
204
|
...(patch.latestAttemptId === null
|
|
219
205
|
? {}
|
|
220
206
|
: patch.latestAttemptId !== undefined
|
|
@@ -280,17 +266,15 @@ function deriveApprovalStatus(response: Record<string, unknown>): 'approved' | '
|
|
|
280
266
|
|
|
281
267
|
class PlanExecutorService {
|
|
282
268
|
async submitNodeResult(params: {
|
|
283
|
-
|
|
269
|
+
threadId: RecordIdInput
|
|
284
270
|
runId: string
|
|
285
271
|
nodeId: string
|
|
286
272
|
emittedBy: string
|
|
287
273
|
result: PlanNodeResultSubmission
|
|
288
274
|
}): Promise<ExecutionPlanToolResultData> {
|
|
289
275
|
const run = await planRunService.getRunById(params.runId)
|
|
290
|
-
if (
|
|
291
|
-
|
|
292
|
-
) {
|
|
293
|
-
throw new Error('Execution node result targets a different workstream.')
|
|
276
|
+
if (recordIdToString(run.threadId, TABLES.THREAD) !== recordIdToString(params.threadId, TABLES.THREAD)) {
|
|
277
|
+
throw new Error('Execution node result targets a different thread.')
|
|
294
278
|
}
|
|
295
279
|
if (run.status === 'completed' || run.status === 'failed' || run.status === 'aborted') {
|
|
296
280
|
throw new Error('Execution run is no longer active.')
|
|
@@ -375,8 +359,7 @@ class PlanExecutorService {
|
|
|
375
359
|
attemptCount: nodeRun.attemptCount + 1,
|
|
376
360
|
latestAttemptId: finalizedAttempt.id,
|
|
377
361
|
latestStructuredOutput: params.result.structuredOutput ?? null,
|
|
378
|
-
latestNotes: params.result.notes
|
|
379
|
-
handoffContext: params.result.handoffContext ?? null,
|
|
362
|
+
latestNotes: params.result.notes,
|
|
380
363
|
}),
|
|
381
364
|
)
|
|
382
365
|
.output('after'),
|
|
@@ -659,8 +642,7 @@ class PlanExecutorService {
|
|
|
659
642
|
status: validation.warnings.length > 0 ? 'partial' : 'completed',
|
|
660
643
|
latestAttemptId: finalizedAttempt.id,
|
|
661
644
|
latestStructuredOutput: params.result.structuredOutput ?? null,
|
|
662
|
-
latestNotes: params.result.notes
|
|
663
|
-
handoffContext: params.result.handoffContext ?? null,
|
|
645
|
+
latestNotes: params.result.notes,
|
|
664
646
|
blockedReason: null,
|
|
665
647
|
failureClass: null,
|
|
666
648
|
completedAt: new Date(),
|
|
@@ -751,18 +733,17 @@ class PlanExecutorService {
|
|
|
751
733
|
action: 'node-result-submitted',
|
|
752
734
|
plan: snapshot,
|
|
753
735
|
message: `Submitted result for node "${nodeSpec.label}".`,
|
|
754
|
-
changedNodeId: params.nodeId,
|
|
755
736
|
})
|
|
756
737
|
}
|
|
757
738
|
|
|
758
739
|
async submitHumanNodeResponse(params: {
|
|
759
|
-
|
|
740
|
+
threadId: RecordIdInput
|
|
760
741
|
approvalId?: string
|
|
761
742
|
respondedBy: string
|
|
762
743
|
response: Record<string, unknown>
|
|
763
744
|
approvalMessageId?: string
|
|
764
745
|
}): Promise<SerializableExecutionPlan | null> {
|
|
765
|
-
const run = await planRunService.getActiveRunRecord(params.
|
|
746
|
+
const run = await planRunService.getActiveRunRecord(params.threadId)
|
|
766
747
|
if (!run || run.status !== 'awaiting-human' || !run.waitingNodeId) {
|
|
767
748
|
return null
|
|
768
749
|
}
|
|
@@ -790,7 +771,7 @@ class PlanExecutorService {
|
|
|
790
771
|
result: {
|
|
791
772
|
structuredOutput: params.response,
|
|
792
773
|
artifacts: [],
|
|
793
|
-
notes: typeof params.response.comments === 'string' ? params.response.comments :
|
|
774
|
+
notes: typeof params.response.comments === 'string' ? params.response.comments : 'Human response submitted.',
|
|
794
775
|
},
|
|
795
776
|
})
|
|
796
777
|
const emittedEvents: PlanEventRecord[] = []
|
|
@@ -818,7 +799,7 @@ class PlanExecutorService {
|
|
|
818
799
|
result: {
|
|
819
800
|
structuredOutput: params.response,
|
|
820
801
|
artifacts: [],
|
|
821
|
-
notes: typeof params.response.comments === 'string' ? params.response.comments :
|
|
802
|
+
notes: typeof params.response.comments === 'string' ? params.response.comments : 'Human response submitted.',
|
|
822
803
|
},
|
|
823
804
|
status: validation.blocking.length > 0 ? 'failed' : 'completed',
|
|
824
805
|
failureClass: validation.failureClass,
|
|
@@ -860,7 +841,6 @@ class PlanExecutorService {
|
|
|
860
841
|
latestAttemptId: attempt.id,
|
|
861
842
|
latestStructuredOutput: params.response,
|
|
862
843
|
latestNotes: typeof params.response.comments === 'string' ? params.response.comments : null,
|
|
863
|
-
handoffContext: null,
|
|
864
844
|
blockedReason: validation.blocking[0]?.message ?? null,
|
|
865
845
|
failureClass: validation.failureClass,
|
|
866
846
|
}),
|
|
@@ -877,7 +857,6 @@ class PlanExecutorService {
|
|
|
877
857
|
latestAttemptId: attempt.id,
|
|
878
858
|
latestStructuredOutput: params.response,
|
|
879
859
|
latestNotes: typeof params.response.comments === 'string' ? params.response.comments : null,
|
|
880
|
-
handoffContext: null,
|
|
881
860
|
blockedReason: null,
|
|
882
861
|
failureClass: null,
|
|
883
862
|
completedAt: new Date(),
|
|
@@ -981,7 +960,7 @@ class PlanExecutorService {
|
|
|
981
960
|
}
|
|
982
961
|
|
|
983
962
|
async resumeRun(params: {
|
|
984
|
-
|
|
963
|
+
threadId: RecordIdInput
|
|
985
964
|
runId: string
|
|
986
965
|
emittedBy: string
|
|
987
966
|
}): Promise<ExecutionPlanToolResultData> {
|
|
@@ -1106,7 +1085,7 @@ class PlanExecutorService {
|
|
|
1106
1085
|
}
|
|
1107
1086
|
|
|
1108
1087
|
async blockNodeOnDispatchFailure(params: {
|
|
1109
|
-
|
|
1088
|
+
threadId: RecordIdInput
|
|
1110
1089
|
runId: string
|
|
1111
1090
|
nodeId: string
|
|
1112
1091
|
emittedBy: string
|
|
@@ -1297,7 +1276,7 @@ class PlanExecutorService {
|
|
|
1297
1276
|
if (params.spec.dependencies && params.spec.dependencies.length > 0) {
|
|
1298
1277
|
const { unresolved } = await planCoordinationService.resolveDependencies({
|
|
1299
1278
|
dependencies: params.spec.dependencies,
|
|
1300
|
-
|
|
1279
|
+
threadId: recordIdToString(params.spec.threadId, TABLES.THREAD),
|
|
1301
1280
|
})
|
|
1302
1281
|
if (unresolved.length > 0) {
|
|
1303
1282
|
currentRun = await this.replaceRun(params.tx, currentRun, { status: 'blocked', readyNodeIds: [] })
|
|
@@ -1405,7 +1384,7 @@ class PlanExecutorService {
|
|
|
1405
1384
|
replaceNodeRun(scheduledNodeRun)
|
|
1406
1385
|
await planSchedulerService.createSchedule({
|
|
1407
1386
|
organizationId: currentRun.organizationId,
|
|
1408
|
-
|
|
1387
|
+
threadId: currentRun.threadId,
|
|
1409
1388
|
planSpecId: params.spec.id,
|
|
1410
1389
|
runId: currentRun.id,
|
|
1411
1390
|
nodeId: nodeSpec.nodeId,
|
|
@@ -1886,7 +1865,14 @@ class PlanExecutorService {
|
|
|
1886
1865
|
run: PlanRunRecord,
|
|
1887
1866
|
checkpoint: RecordIdInput | { id: RecordIdInput },
|
|
1888
1867
|
) {
|
|
1889
|
-
const checkpointId =
|
|
1868
|
+
const checkpointId =
|
|
1869
|
+
checkpoint &&
|
|
1870
|
+
typeof checkpoint === 'object' &&
|
|
1871
|
+
!(checkpoint instanceof RecordId) &&
|
|
1872
|
+
!(checkpoint instanceof StringRecordId) &&
|
|
1873
|
+
'id' in checkpoint
|
|
1874
|
+
? (checkpoint as { id: RecordIdInput }).id
|
|
1875
|
+
: checkpoint
|
|
1890
1876
|
|
|
1891
1877
|
await tx
|
|
1892
1878
|
.update(ensureRecordId(run.id, TABLES.PLAN_RUN))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExecutionPlanToolResultData, PlanRunRecord } from '@lota-sdk/shared'
|
|
1
|
+
import type { ExecutionPlanToolResultData, PlanRunRecord, SerializableExecutionPlan } from '@lota-sdk/shared'
|
|
2
2
|
|
|
3
3
|
import type { RecordIdInput } from '../db/record-id'
|
|
4
4
|
import { ensureRecordId } from '../db/record-id'
|
|
@@ -21,7 +21,7 @@ export function toRunData(run: PlanRunRecord, patch: PlanRunUpdate) {
|
|
|
21
21
|
return {
|
|
22
22
|
planSpecId: ensureRecordId(run.planSpecId, TABLES.PLAN_SPEC),
|
|
23
23
|
organizationId: ensureRecordId(run.organizationId, TABLES.ORGANIZATION),
|
|
24
|
-
|
|
24
|
+
threadId: ensureRecordId(run.threadId, TABLES.THREAD),
|
|
25
25
|
leadAgentId: patch.leadAgentId ?? run.leadAgentId,
|
|
26
26
|
status: patch.status ?? run.status,
|
|
27
27
|
...(patch.currentNodeId === null
|
|
@@ -71,18 +71,37 @@ export function toRunData(run: PlanRunRecord, patch: PlanRunUpdate) {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function toSlimPlanSummary(plan: SerializableExecutionPlan): NonNullable<ExecutionPlanToolResultData['plan']> {
|
|
75
|
+
const completed = plan.progress.completed + plan.progress.partial
|
|
76
|
+
return {
|
|
77
|
+
runId: plan.runId,
|
|
78
|
+
title: plan.title,
|
|
79
|
+
objective: plan.objective,
|
|
80
|
+
status: plan.status,
|
|
81
|
+
progress: { completed, total: plan.progress.total },
|
|
82
|
+
nodes: plan.nodes.map((node) => ({
|
|
83
|
+
id: node.id,
|
|
84
|
+
label: node.label,
|
|
85
|
+
type: node.type,
|
|
86
|
+
status: node.status,
|
|
87
|
+
ownerRef: node.owner.ref,
|
|
88
|
+
})),
|
|
89
|
+
activeNodeIds: plan.activeNodeIds,
|
|
90
|
+
readyNodeIds: plan.readyNodeIds,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
74
94
|
export function buildExecutionPlanToolResult(params: {
|
|
75
95
|
action: ExecutionPlanToolResultData['action']
|
|
76
|
-
plan:
|
|
96
|
+
plan: SerializableExecutionPlan | null
|
|
77
97
|
message: string
|
|
78
|
-
changedNodeId?: string
|
|
79
98
|
}): ExecutionPlanToolResultData {
|
|
99
|
+
const slim = params.plan ? toSlimPlanSummary(params.plan) : null
|
|
80
100
|
return {
|
|
81
101
|
action: params.action,
|
|
82
102
|
message: params.message,
|
|
83
|
-
...(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
status: params.plan?.status,
|
|
103
|
+
...(slim ? { plan: slim } : {}),
|
|
104
|
+
hasPlan: slim !== null,
|
|
105
|
+
status: slim?.status,
|
|
87
106
|
}
|
|
88
107
|
}
|
|
@@ -168,10 +168,10 @@ class PlanRunService {
|
|
|
168
168
|
return spec
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
async
|
|
171
|
+
async listPlanSpecsByThread(threadId: RecordIdInput): Promise<PlanSpecRecord[]> {
|
|
172
172
|
return databaseService.findMany(
|
|
173
173
|
TABLES.PLAN_SPEC,
|
|
174
|
-
{
|
|
174
|
+
{ threadId: ensureRecordId(threadId, TABLES.THREAD) },
|
|
175
175
|
PlanSpecSchema,
|
|
176
176
|
{ orderBy: 'createdAt', orderDir: 'DESC' },
|
|
177
177
|
)
|
|
@@ -219,15 +219,15 @@ class PlanRunService {
|
|
|
219
219
|
return run
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
async getActiveRunRecord(
|
|
223
|
-
const runs = await this.getActiveRunRecords(
|
|
222
|
+
async getActiveRunRecord(threadId: RecordIdInput): Promise<PlanRunRecord | null> {
|
|
223
|
+
const runs = await this.getActiveRunRecords(threadId)
|
|
224
224
|
return runs[0] ?? null
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
async getActiveRunRecords(
|
|
227
|
+
async getActiveRunRecords(threadId: RecordIdInput): Promise<PlanRunRecord[]> {
|
|
228
228
|
const runs = await databaseService.findMany(
|
|
229
229
|
TABLES.PLAN_RUN,
|
|
230
|
-
{
|
|
230
|
+
{ threadId: ensureRecordId(threadId, TABLES.THREAD) },
|
|
231
231
|
PlanRunSchema,
|
|
232
232
|
{ orderBy: 'updatedAt', orderDir: 'DESC' },
|
|
233
233
|
)
|
|
@@ -379,7 +379,6 @@ class PlanRunService {
|
|
|
379
379
|
status: nodeRun.status,
|
|
380
380
|
upstreamNodeIds: [...nodeSpec.upstreamNodeIds],
|
|
381
381
|
downstreamNodeIds: [...nodeSpec.downstreamNodeIds],
|
|
382
|
-
...(nodeRun.handoffContext ? { handoffContext: nodeRun.handoffContext } : {}),
|
|
383
382
|
...(nodeRun.completedAt ? { completedAt: toOptionalIsoDateTimeString(nodeRun.completedAt) } : {}),
|
|
384
383
|
} as SerializablePlanNode
|
|
385
384
|
}
|
|
@@ -418,7 +417,6 @@ class PlanRunService {
|
|
|
418
417
|
resolvedInput: nodeRun.resolvedInput,
|
|
419
418
|
latestStructuredOutput: nodeRun.latestStructuredOutput,
|
|
420
419
|
latestNotes: nodeRun.latestNotes,
|
|
421
|
-
handoffContext: nodeRun.handoffContext,
|
|
422
420
|
blockedReason: nodeRun.blockedReason,
|
|
423
421
|
failureClass: nodeRun.failureClass,
|
|
424
422
|
upstreamNodeIds: [...nodeSpec.upstreamNodeIds],
|
|
@@ -432,7 +430,7 @@ class PlanRunService {
|
|
|
432
430
|
return {
|
|
433
431
|
specId: recordIdToString(spec.id, TABLES.PLAN_SPEC),
|
|
434
432
|
runId: recordIdToString(run.id, TABLES.PLAN_RUN),
|
|
435
|
-
|
|
433
|
+
threadId: recordIdToString(run.threadId, TABLES.THREAD),
|
|
436
434
|
organizationId: recordIdToString(run.organizationId, TABLES.ORGANIZATION),
|
|
437
435
|
title: spec.title,
|
|
438
436
|
objective: spec.objective,
|
|
@@ -41,7 +41,7 @@ class PlanSchedulerService {
|
|
|
41
41
|
|
|
42
42
|
async createSchedule(params: {
|
|
43
43
|
organizationId: RecordIdInput
|
|
44
|
-
|
|
44
|
+
threadId: RecordIdInput
|
|
45
45
|
planSpecId?: RecordIdInput
|
|
46
46
|
runId?: RecordIdInput
|
|
47
47
|
nodeId?: string
|
|
@@ -54,7 +54,7 @@ class PlanSchedulerService {
|
|
|
54
54
|
TABLES.PLAN_SCHEDULE,
|
|
55
55
|
{
|
|
56
56
|
organizationId: ensureRecordId(params.organizationId, TABLES.ORGANIZATION),
|
|
57
|
-
|
|
57
|
+
threadId: ensureRecordId(params.threadId, TABLES.THREAD),
|
|
58
58
|
planSpecId: params.planSpecId ? ensureRecordId(params.planSpecId, TABLES.PLAN_SPEC) : undefined,
|
|
59
59
|
runId: params.runId ? ensureRecordId(params.runId, TABLES.PLAN_RUN) : undefined,
|
|
60
60
|
nodeId: params.nodeId,
|
|
@@ -227,10 +227,10 @@ class PlanSchedulerService {
|
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
async listSchedules(
|
|
230
|
+
async listSchedules(threadId: RecordIdInput): Promise<PlanScheduleRecord[]> {
|
|
231
231
|
return databaseService.findMany(
|
|
232
232
|
TABLES.PLAN_SCHEDULE,
|
|
233
|
-
{
|
|
233
|
+
{ threadId: ensureRecordId(threadId, TABLES.THREAD) },
|
|
234
234
|
PlanScheduleRecordSchema,
|
|
235
235
|
{ orderBy: 'createdAt', orderDir: 'ASC' },
|
|
236
236
|
)
|
|
@@ -87,7 +87,7 @@ class PlanTemplateService {
|
|
|
87
87
|
async instantiate(params: {
|
|
88
88
|
templateId: RecordIdInput
|
|
89
89
|
organizationId: RecordIdInput
|
|
90
|
-
|
|
90
|
+
threadId: RecordIdInput
|
|
91
91
|
leadAgentId: string
|
|
92
92
|
overrides?: Partial<PlanDraft>
|
|
93
93
|
carryForwardArtifacts?: PlanArtifactRecord[]
|
|
@@ -106,7 +106,7 @@ class PlanTemplateService {
|
|
|
106
106
|
|
|
107
107
|
return executionPlanService.createPlan({
|
|
108
108
|
organizationId: params.organizationId,
|
|
109
|
-
|
|
109
|
+
threadId: params.threadId,
|
|
110
110
|
leadAgentId: params.leadAgentId,
|
|
111
111
|
input: draft,
|
|
112
112
|
})
|
|
@@ -582,16 +582,6 @@ class PlanValidatorService {
|
|
|
582
582
|
}
|
|
583
583
|
|
|
584
584
|
if (deliverable.schemaRef) {
|
|
585
|
-
if (artifact.schemaRef !== deliverable.schemaRef) {
|
|
586
|
-
blocking.push(
|
|
587
|
-
createIssue({
|
|
588
|
-
code: 'artifact_schema_mismatch',
|
|
589
|
-
message: `Artifact "${deliverable.name}" must declare schemaRef "${deliverable.schemaRef}".`,
|
|
590
|
-
nodeId: params.node.id,
|
|
591
|
-
}),
|
|
592
|
-
)
|
|
593
|
-
}
|
|
594
|
-
|
|
595
585
|
const artifactSchema = resolveSchemaRef(params.draft, deliverable.schemaRef)
|
|
596
586
|
if (!artifact.payload) {
|
|
597
587
|
blocking.push(
|
|
@@ -658,7 +648,6 @@ class PlanValidatorService {
|
|
|
658
648
|
if (check.type === 'schema') {
|
|
659
649
|
const schemaRef =
|
|
660
650
|
(typeof check.config.schemaRef === 'string' ? check.config.schemaRef : undefined) ??
|
|
661
|
-
artifact?.schemaRef ??
|
|
662
651
|
(artifactName ? node.deliverables.find((candidate) => candidate.name === artifactName)?.schemaRef : undefined)
|
|
663
652
|
if (!schemaRef) {
|
|
664
653
|
return createIssue({
|
|
@@ -20,7 +20,7 @@ function buildPluginExecutionParams(params: {
|
|
|
20
20
|
inputs: params.resolvedInput,
|
|
21
21
|
context: {
|
|
22
22
|
organizationId: params.context.organizationId,
|
|
23
|
-
|
|
23
|
+
threadId: params.context.threadId,
|
|
24
24
|
planId: params.context.planId,
|
|
25
25
|
nodeId: params.context.nodeId,
|
|
26
26
|
...(params.context.userId ? { userId: params.context.userId } : {}),
|
|
@@ -141,7 +141,7 @@ function extractJobContext(data: unknown): Record<string, unknown> | undefined {
|
|
|
141
141
|
|
|
142
142
|
const context = compactRecord({
|
|
143
143
|
organizationId: readStringField(record, 'organizationId') ?? readStringField(record, 'orgId'),
|
|
144
|
-
|
|
144
|
+
threadId: readStringField(record, 'threadId'),
|
|
145
145
|
userId: readStringField(record, 'userId'),
|
|
146
146
|
agentId: readStringField(record, 'agentId'),
|
|
147
147
|
sourceId: readStringField(record, 'sourceId'),
|
|
@@ -18,7 +18,7 @@ function buildRefinementPromptInput(
|
|
|
18
18
|
`sourceLabel=${candidate.sourceLabel}`,
|
|
19
19
|
`systemTitle=${candidate.systemTitle}`,
|
|
20
20
|
metadata.agentName ? `agentName=${metadata.agentName}` : null,
|
|
21
|
-
metadata.
|
|
21
|
+
metadata.threadTitle ? `threadTitle=${metadata.threadTitle}` : null,
|
|
22
22
|
metadata.userMessageText ? `userMessage=${metadata.userMessageText}` : null,
|
|
23
23
|
metadata.assistantSummary ? `assistantSummary=${metadata.assistantSummary}` : null,
|
|
24
24
|
].filter((line): line is string => Boolean(line))
|
|
@@ -79,9 +79,9 @@ function shouldKeepExistingAgentTitle(existing: RecentActivityRow | null): boole
|
|
|
79
79
|
function buildRecentActivityAreaKey(
|
|
80
80
|
row: Pick<RecentActivityRow, 'targetKind' | 'targetId' | 'kind' | 'mergeKey' | 'metadata'>,
|
|
81
81
|
): string {
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
return `
|
|
82
|
+
const threadId = row.metadata?.threadId
|
|
83
|
+
if (threadId) {
|
|
84
|
+
return `thread:${compactWhitespace(threadId)}`
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
if (row.targetId) {
|
|
@@ -373,7 +373,7 @@ class RecentActivityService {
|
|
|
373
373
|
'chat',
|
|
374
374
|
'agent task',
|
|
375
375
|
'recent activity',
|
|
376
|
-
'
|
|
376
|
+
'thread update',
|
|
377
377
|
])
|
|
378
378
|
|
|
379
379
|
return !bannedTitles.has(normalizedCandidate)
|
|
@@ -8,7 +8,7 @@ const BUILT_IN_SYSTEM_EXECUTORS = Object.freeze({
|
|
|
8
8
|
'plan-runtime': {
|
|
9
9
|
supportedOperations: ['echo-input'],
|
|
10
10
|
async executeNode(params: PluginNodeExecutionParams): Promise<PlanNodeResult> {
|
|
11
|
-
return { structuredOutput: structuredClone(params.inputs), artifacts: [] }
|
|
11
|
+
return { notes: 'System echo-input completed.', structuredOutput: structuredClone(params.inputs), artifacts: [] }
|
|
12
12
|
},
|
|
13
13
|
} satisfies SystemNodeExecutor,
|
|
14
14
|
})
|
|
@@ -36,7 +36,7 @@ function buildSystemExecutionParams(params: {
|
|
|
36
36
|
inputs: params.resolvedInput,
|
|
37
37
|
context: {
|
|
38
38
|
organizationId: params.context.organizationId,
|
|
39
|
-
|
|
39
|
+
threadId: params.context.threadId,
|
|
40
40
|
planId: params.context.planId,
|
|
41
41
|
nodeId: params.context.nodeId,
|
|
42
42
|
...(params.context.userId ? { userId: params.context.userId } : {}),
|