@cleocode/core 2026.3.43 → 2026.3.45
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/admin/export-tasks.d.ts.map +1 -1
- package/dist/admin/import-tasks.d.ts +10 -2
- package/dist/admin/import-tasks.d.ts.map +1 -1
- package/dist/agents/agent-schema.d.ts +358 -0
- package/dist/agents/agent-schema.d.ts.map +1 -0
- package/dist/agents/capacity.d.ts +57 -0
- package/dist/agents/capacity.d.ts.map +1 -0
- package/dist/agents/index.d.ts +17 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/registry.d.ts +115 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/retry.d.ts +83 -0
- package/dist/agents/retry.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +4 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/payload-schemas.d.ts +214 -0
- package/dist/hooks/payload-schemas.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16937 -2371
- package/dist/index.js.map +4 -4
- package/dist/inject/index.d.ts.map +1 -1
- package/dist/intelligence/impact.d.ts +51 -0
- package/dist/intelligence/impact.d.ts.map +1 -0
- package/dist/intelligence/index.d.ts +15 -0
- package/dist/intelligence/index.d.ts.map +1 -0
- package/dist/intelligence/patterns.d.ts +66 -0
- package/dist/intelligence/patterns.d.ts.map +1 -0
- package/dist/intelligence/prediction.d.ts +51 -0
- package/dist/intelligence/prediction.d.ts.map +1 -0
- package/dist/intelligence/types.d.ts +221 -0
- package/dist/intelligence/types.d.ts.map +1 -0
- package/dist/internal.d.ts +12 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/issue/template-parser.d.ts +8 -2
- package/dist/issue/template-parser.d.ts.map +1 -1
- package/dist/lifecycle/pipeline.d.ts +2 -2
- package/dist/lifecycle/pipeline.d.ts.map +1 -1
- package/dist/lifecycle/state-machine.d.ts +1 -1
- package/dist/lifecycle/state-machine.d.ts.map +1 -1
- package/dist/memory/brain-lifecycle.d.ts.map +1 -1
- package/dist/memory/brain-retrieval.d.ts.map +1 -1
- package/dist/memory/brain-row-types.d.ts +40 -6
- package/dist/memory/brain-row-types.d.ts.map +1 -1
- package/dist/memory/brain-search.d.ts.map +1 -1
- package/dist/memory/brain-similarity.d.ts.map +1 -1
- package/dist/memory/claude-mem-migration.d.ts.map +1 -1
- package/dist/nexus/discover.d.ts.map +1 -1
- package/dist/nexus/index.d.ts +2 -0
- package/dist/nexus/index.d.ts.map +1 -1
- package/dist/nexus/transfer-types.d.ts +123 -0
- package/dist/nexus/transfer-types.d.ts.map +1 -0
- package/dist/nexus/transfer.d.ts +31 -0
- package/dist/nexus/transfer.d.ts.map +1 -0
- package/dist/orchestration/bootstrap.d.ts.map +1 -1
- package/dist/orchestration/skill-ops.d.ts +4 -4
- package/dist/orchestration/skill-ops.d.ts.map +1 -1
- package/dist/otel/index.d.ts +1 -1
- package/dist/otel/index.d.ts.map +1 -1
- package/dist/sessions/briefing.d.ts.map +1 -1
- package/dist/sessions/handoff.d.ts.map +1 -1
- package/dist/sessions/index.d.ts +1 -1
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/types.d.ts +8 -42
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/signaldock/signaldock-transport.d.ts +1 -1
- package/dist/signaldock/signaldock-transport.d.ts.map +1 -1
- package/dist/skills/injection/subagent.d.ts +3 -3
- package/dist/skills/injection/subagent.d.ts.map +1 -1
- package/dist/skills/manifests/contribution.d.ts +2 -2
- package/dist/skills/manifests/contribution.d.ts.map +1 -1
- package/dist/skills/orchestrator/spawn.d.ts +6 -6
- package/dist/skills/orchestrator/spawn.d.ts.map +1 -1
- package/dist/skills/orchestrator/startup.d.ts +1 -1
- package/dist/skills/orchestrator/startup.d.ts.map +1 -1
- package/dist/skills/orchestrator/validator.d.ts +2 -2
- package/dist/skills/orchestrator/validator.d.ts.map +1 -1
- package/dist/skills/precedence-types.d.ts +24 -1
- package/dist/skills/precedence-types.d.ts.map +1 -1
- package/dist/skills/types.d.ts +70 -4
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/store/brain-sqlite.d.ts +4 -1
- package/dist/store/brain-sqlite.d.ts.map +1 -1
- package/dist/store/export.d.ts +5 -4
- package/dist/store/export.d.ts.map +1 -1
- package/dist/store/nexus-sqlite.d.ts +4 -1
- package/dist/store/nexus-sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.d.ts +4 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/tasks-schema.d.ts +14 -4
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/typed-query.d.ts +12 -0
- package/dist/store/typed-query.d.ts.map +1 -0
- package/dist/store/validation-schemas.d.ts +2423 -50
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/system/inject-generate.d.ts.map +1 -1
- package/dist/validation/doctor/checks.d.ts +5 -0
- package/dist/validation/doctor/checks.d.ts.map +1 -1
- package/dist/validation/engine.d.ts +10 -10
- package/dist/validation/engine.d.ts.map +1 -1
- package/dist/validation/index.d.ts +6 -2
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/protocol-common.d.ts +10 -2
- package/dist/validation/protocol-common.d.ts.map +1 -1
- package/migrations/drizzle-tasks/20260320013731_wave0-schema-hardening/migration.sql +84 -0
- package/migrations/drizzle-tasks/20260320013731_wave0-schema-hardening/snapshot.json +4060 -0
- package/migrations/drizzle-tasks/20260320020000_agent-dimension/migration.sql +35 -0
- package/migrations/drizzle-tasks/20260320020000_agent-dimension/snapshot.json +4312 -0
- package/package.json +2 -2
- package/src/admin/export-tasks.ts +2 -5
- package/src/admin/import-tasks.ts +53 -29
- package/src/agents/__tests__/capacity.test.ts +219 -0
- package/src/agents/__tests__/registry.test.ts +457 -0
- package/src/agents/__tests__/retry.test.ts +289 -0
- package/src/agents/agent-schema.ts +107 -0
- package/src/agents/capacity.ts +151 -0
- package/src/agents/index.ts +68 -0
- package/src/agents/registry.ts +449 -0
- package/src/agents/retry.ts +255 -0
- package/src/hooks/index.ts +20 -1
- package/src/hooks/payload-schemas.ts +199 -0
- package/src/index.ts +69 -0
- package/src/inject/index.ts +14 -14
- package/src/intelligence/__tests__/impact.test.ts +453 -0
- package/src/intelligence/__tests__/patterns.test.ts +450 -0
- package/src/intelligence/__tests__/prediction.test.ts +418 -0
- package/src/intelligence/impact.ts +638 -0
- package/src/intelligence/index.ts +47 -0
- package/src/intelligence/patterns.ts +621 -0
- package/src/intelligence/prediction.ts +621 -0
- package/src/intelligence/types.ts +273 -0
- package/src/internal.ts +89 -2
- package/src/issue/template-parser.ts +65 -4
- package/src/lifecycle/pipeline.ts +14 -7
- package/src/lifecycle/state-machine.ts +6 -2
- package/src/memory/brain-lifecycle.ts +5 -11
- package/src/memory/brain-retrieval.ts +44 -38
- package/src/memory/brain-row-types.ts +43 -6
- package/src/memory/brain-search.ts +53 -32
- package/src/memory/brain-similarity.ts +9 -8
- package/src/memory/claude-mem-migration.ts +4 -3
- package/src/nexus/__tests__/nexus-e2e.test.ts +1481 -0
- package/src/nexus/__tests__/transfer.test.ts +446 -0
- package/src/nexus/discover.ts +1 -0
- package/src/nexus/index.ts +14 -0
- package/src/nexus/transfer-types.ts +129 -0
- package/src/nexus/transfer.ts +314 -0
- package/src/orchestration/bootstrap.ts +11 -17
- package/src/orchestration/skill-ops.ts +52 -32
- package/src/otel/index.ts +48 -4
- package/src/sessions/__tests__/briefing.test.ts +31 -2
- package/src/sessions/briefing.ts +27 -42
- package/src/sessions/handoff.ts +52 -86
- package/src/sessions/index.ts +5 -1
- package/src/sessions/types.ts +9 -43
- package/src/signaldock/signaldock-transport.ts +5 -2
- package/src/skills/injection/subagent.ts +10 -16
- package/src/skills/manifests/contribution.ts +5 -13
- package/src/skills/orchestrator/__tests__/spawn-tier.test.ts +44 -30
- package/src/skills/orchestrator/spawn.ts +18 -31
- package/src/skills/orchestrator/startup.ts +78 -65
- package/src/skills/orchestrator/validator.ts +26 -31
- package/src/skills/precedence-types.ts +24 -1
- package/src/skills/types.ts +72 -5
- package/src/store/__tests__/test-db-helper.d.ts +4 -4
- package/src/store/__tests__/test-db-helper.js +5 -16
- package/src/store/__tests__/test-db-helper.ts +5 -18
- package/src/store/brain-sqlite.ts +7 -3
- package/src/store/chain-schema.ts +1 -1
- package/src/store/export.ts +22 -12
- package/src/store/nexus-sqlite.ts +7 -3
- package/src/store/sqlite.ts +9 -3
- package/src/store/tasks-schema.ts +65 -8
- package/src/store/typed-query.ts +17 -0
- package/src/store/validation-schemas.ts +347 -23
- package/src/system/inject-generate.ts +9 -23
- package/src/validation/doctor/checks.ts +24 -2
- package/src/validation/engine.ts +11 -11
- package/src/validation/index.ts +131 -3
- package/src/validation/protocol-common.ts +54 -3
- package/dist/tasks/reparent.d.ts +0 -38
- package/dist/tasks/reparent.d.ts.map +0 -1
- package/src/tasks/reparent.ts +0 -134
package/src/sessions/briefing.ts
CHANGED
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
* @epic T4914
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import type {
|
|
20
|
+
import type { Task, TaskWorkState } from '@cleocode/contracts';
|
|
21
21
|
import type { SessionMemoryContext } from '../memory/session-memory.js';
|
|
22
22
|
import type { DataAccessor } from '../store/data-accessor.js';
|
|
23
23
|
import { getAccessor } from '../store/data-accessor.js';
|
|
24
24
|
import { depsReady } from '../tasks/deps-ready.js';
|
|
25
25
|
import { getLastHandoff, type HandoffData } from './handoff.js';
|
|
26
|
-
import type {
|
|
26
|
+
import type { TaskWorkStateExt } from './types.js';
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Task summary for briefing output.
|
|
@@ -138,15 +138,9 @@ export async function computeBriefing(
|
|
|
138
138
|
): Promise<SessionBriefing> {
|
|
139
139
|
const accessor = await getAccessor(projectRoot);
|
|
140
140
|
const { tasks } = await accessor.queryTasks({});
|
|
141
|
-
const focus = await accessor.getMetaValue<TaskWorkState>('focus_state')
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// Build a TaskFileExt-compatible shape from targeted queries
|
|
145
|
-
const current = {
|
|
146
|
-
tasks,
|
|
147
|
-
focus: focus ?? undefined,
|
|
148
|
-
_meta: fileMeta ?? undefined,
|
|
149
|
-
} as unknown as TaskFileExt;
|
|
141
|
+
const focus = (await accessor.getMetaValue<TaskWorkState>('focus_state')) as
|
|
142
|
+
| TaskWorkStateExt
|
|
143
|
+
| undefined;
|
|
150
144
|
|
|
151
145
|
// Build task map for quick lookups
|
|
152
146
|
const taskMap = new Map(tasks.map((t) => [t.id, t]));
|
|
@@ -155,19 +149,16 @@ export async function computeBriefing(
|
|
|
155
149
|
const scopeFilter = await parseScope(options.scope, accessor);
|
|
156
150
|
|
|
157
151
|
// Compute in-scope task IDs (undefined = all tasks in scope)
|
|
158
|
-
const scopeTaskIds = getScopeTaskIdSet(
|
|
159
|
-
scopeFilter,
|
|
160
|
-
tasks as unknown as Array<{ id: string; parentId?: string; [key: string]: unknown }>,
|
|
161
|
-
);
|
|
152
|
+
const scopeTaskIds = getScopeTaskIdSet(scopeFilter, tasks);
|
|
162
153
|
|
|
163
154
|
// 1. Last session handoff
|
|
164
155
|
const lastSession = await computeLastSession(projectRoot, scopeFilter);
|
|
165
156
|
|
|
166
157
|
// 2. Current active task
|
|
167
|
-
const currentTaskInfo = computeCurrentTask(
|
|
158
|
+
const currentTaskInfo = computeCurrentTask(focus, taskMap);
|
|
168
159
|
|
|
169
160
|
// 3. Next tasks (leverage-scored)
|
|
170
|
-
const nextTasks = computeNextTasks(tasks, taskMap,
|
|
161
|
+
const nextTasks = computeNextTasks(tasks, taskMap, focus, {
|
|
171
162
|
maxTasks: options.maxNextTasks ?? 5,
|
|
172
163
|
scopeTaskIds,
|
|
173
164
|
});
|
|
@@ -191,7 +182,7 @@ export async function computeBriefing(
|
|
|
191
182
|
});
|
|
192
183
|
|
|
193
184
|
// 7. Pipeline stage (optional - may not be available)
|
|
194
|
-
const pipelineStage = computePipelineStage(
|
|
185
|
+
const pipelineStage = await computePipelineStage(focus);
|
|
195
186
|
|
|
196
187
|
// 8. Brain memory context (optional, best-effort)
|
|
197
188
|
let memoryContext: SessionMemoryContext | undefined;
|
|
@@ -259,7 +250,7 @@ async function parseScope(
|
|
|
259
250
|
*/
|
|
260
251
|
function getScopeTaskIdSet(
|
|
261
252
|
scopeFilter: { type: 'global' | 'epic'; epicId?: string } | undefined,
|
|
262
|
-
tasks:
|
|
253
|
+
tasks: Task[],
|
|
263
254
|
): Set<string> | undefined {
|
|
264
255
|
if (!scopeFilter || scopeFilter.type === 'global') {
|
|
265
256
|
return undefined; // All tasks in scope
|
|
@@ -325,10 +316,10 @@ async function computeLastSession(
|
|
|
325
316
|
* Compute current active task from task file.
|
|
326
317
|
*/
|
|
327
318
|
function computeCurrentTask(
|
|
328
|
-
|
|
319
|
+
focus: TaskWorkStateExt | undefined,
|
|
329
320
|
taskMap: Map<string, unknown>,
|
|
330
321
|
): CurrentTaskInfo | null {
|
|
331
|
-
const focusTaskId =
|
|
322
|
+
const focusTaskId = focus?.currentTask;
|
|
332
323
|
if (!focusTaskId) return null;
|
|
333
324
|
|
|
334
325
|
const task = taskMap.get(focusTaskId) as
|
|
@@ -376,7 +367,7 @@ function calculateLeverage(taskId: string, taskMap: Map<string, unknown>): numbe
|
|
|
376
367
|
function computeNextTasks(
|
|
377
368
|
tasks: unknown[],
|
|
378
369
|
taskMap: Map<string, unknown>,
|
|
379
|
-
|
|
370
|
+
focus: TaskWorkStateExt | undefined,
|
|
380
371
|
options: { maxTasks: number; scopeTaskIds?: Set<string> },
|
|
381
372
|
): BriefingTask[] {
|
|
382
373
|
const pendingTasks = tasks.filter((t) => {
|
|
@@ -387,7 +378,7 @@ function computeNextTasks(
|
|
|
387
378
|
});
|
|
388
379
|
|
|
389
380
|
const scored: BriefingTask[] = [];
|
|
390
|
-
const currentPhase =
|
|
381
|
+
const currentPhase = focus?.currentPhase;
|
|
391
382
|
|
|
392
383
|
for (const task of pendingTasks) {
|
|
393
384
|
const t = task as {
|
|
@@ -593,28 +584,22 @@ function calculateEpicCompletion(epicId: string, taskMap: Map<string, unknown>):
|
|
|
593
584
|
/**
|
|
594
585
|
* Compute pipeline stage info from task file metadata.
|
|
595
586
|
*/
|
|
596
|
-
function computePipelineStage(
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
| undefined;
|
|
587
|
+
async function computePipelineStage(
|
|
588
|
+
focus: TaskWorkStateExt | undefined,
|
|
589
|
+
): Promise<PipelineStageInfo | undefined> {
|
|
590
|
+
const taskId = focus?.currentTask;
|
|
591
|
+
if (!taskId) return undefined;
|
|
602
592
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
};
|
|
608
|
-
}
|
|
593
|
+
try {
|
|
594
|
+
const { getPipeline } = await import('../lifecycle/pipeline.js');
|
|
595
|
+
const pipeline = await getPipeline(taskId);
|
|
596
|
+
if (!pipeline) return undefined;
|
|
609
597
|
|
|
610
|
-
// Try from lifecycle state if available
|
|
611
|
-
const lifecycleState = current._meta?.lifecycleState as string | undefined;
|
|
612
|
-
if (lifecycleState) {
|
|
613
598
|
return {
|
|
614
|
-
currentStage:
|
|
615
|
-
stageStatus: 'active',
|
|
599
|
+
currentStage: pipeline.currentStage,
|
|
600
|
+
stageStatus: pipeline.isActive ? 'active' : (pipeline.status ?? 'completed'),
|
|
616
601
|
};
|
|
602
|
+
} catch {
|
|
603
|
+
return undefined;
|
|
617
604
|
}
|
|
618
|
-
|
|
619
|
-
return undefined;
|
|
620
605
|
}
|
package/src/sessions/handoff.ts
CHANGED
|
@@ -15,12 +15,11 @@
|
|
|
15
15
|
|
|
16
16
|
import { execFile } from 'node:child_process';
|
|
17
17
|
import { promisify } from 'node:util';
|
|
18
|
-
import type {
|
|
18
|
+
import type { Session, Task } from '@cleocode/contracts';
|
|
19
19
|
import { ExitCode } from '@cleocode/contracts';
|
|
20
20
|
import { CleoError } from '../errors.js';
|
|
21
21
|
import { getAccessor } from '../store/data-accessor.js';
|
|
22
22
|
import { getDecisionLog } from './decisions.js';
|
|
23
|
-
import type { TaskFileExt } from './types.js';
|
|
24
23
|
|
|
25
24
|
const execFileAsync = promisify(execFile);
|
|
26
25
|
|
|
@@ -77,15 +76,8 @@ export async function computeHandoff(
|
|
|
77
76
|
throw new CleoError(ExitCode.SESSION_NOT_FOUND, `Session '${options.sessionId}' not found`);
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
// Load
|
|
79
|
+
// Load tasks directly from SQLite via DataAccessor
|
|
81
80
|
const { tasks } = await accessor.queryTasks({});
|
|
82
|
-
const focus = await accessor.getMetaValue<TaskWorkState>('focus_state');
|
|
83
|
-
const fileMeta = await accessor.getMetaValue<FileMeta>('file_meta');
|
|
84
|
-
const current = {
|
|
85
|
-
tasks,
|
|
86
|
-
focus: focus ?? undefined,
|
|
87
|
-
_meta: fileMeta ?? undefined,
|
|
88
|
-
} as unknown as TaskFileExt;
|
|
89
81
|
|
|
90
82
|
// Get decisions recorded during this session
|
|
91
83
|
const decisions = await getDecisionLog(projectRoot, { sessionId: options.sessionId });
|
|
@@ -97,9 +89,9 @@ export async function computeHandoff(
|
|
|
97
89
|
tasksCompleted: session.tasksCompleted ?? [],
|
|
98
90
|
tasksCreated: session.tasksCreated ?? [],
|
|
99
91
|
decisionsRecorded: decisions.length,
|
|
100
|
-
nextSuggested: computeNextSuggested(session,
|
|
101
|
-
openBlockers: findOpenBlockers(
|
|
102
|
-
openBugs: findOpenBugs(
|
|
92
|
+
nextSuggested: computeNextSuggested(session, tasks),
|
|
93
|
+
openBlockers: findOpenBlockers(tasks, session),
|
|
94
|
+
openBugs: findOpenBugs(tasks, session),
|
|
103
95
|
};
|
|
104
96
|
|
|
105
97
|
// Apply human overrides
|
|
@@ -117,20 +109,15 @@ export async function computeHandoff(
|
|
|
117
109
|
* Compute top-3 next suggested tasks.
|
|
118
110
|
* Prioritizes uncompleted tasks within the session scope.
|
|
119
111
|
*/
|
|
120
|
-
function computeNextSuggested(session: Session,
|
|
121
|
-
const suggestions: string[] = [];
|
|
122
|
-
|
|
123
|
-
if (!current.tasks) return suggestions;
|
|
124
|
-
|
|
112
|
+
function computeNextSuggested(session: Session, tasks: Task[]): string[] {
|
|
125
113
|
// Filter to tasks in scope
|
|
126
|
-
const scopeTaskIds = getScopeTaskIds(session,
|
|
114
|
+
const scopeTaskIds = getScopeTaskIds(session, tasks);
|
|
127
115
|
|
|
128
116
|
// Get uncompleted tasks in scope
|
|
129
|
-
const pendingTasks =
|
|
117
|
+
const pendingTasks = tasks.filter(
|
|
130
118
|
(t) =>
|
|
131
119
|
scopeTaskIds.has(t.id) &&
|
|
132
120
|
t.status !== 'done' &&
|
|
133
|
-
t.status !== 'completed' &&
|
|
134
121
|
t.status !== 'archived' &&
|
|
135
122
|
t.status !== 'cancelled',
|
|
136
123
|
);
|
|
@@ -145,11 +132,9 @@ function computeNextSuggested(session: Session, current: TaskFileExt): string[]
|
|
|
145
132
|
|
|
146
133
|
pendingTasks.sort((a, b) => {
|
|
147
134
|
const priorityDiff =
|
|
148
|
-
(priorityOrder[a.priority
|
|
135
|
+
(priorityOrder[a.priority ?? 'medium'] ?? 99) - (priorityOrder[b.priority ?? 'medium'] ?? 99);
|
|
149
136
|
if (priorityDiff !== 0) return priorityDiff;
|
|
150
|
-
|
|
151
|
-
const bCreated = typeof b.createdAt === 'string' ? b.createdAt : '1970-01-01T00:00:00Z';
|
|
152
|
-
return new Date(aCreated).getTime() - new Date(bCreated).getTime();
|
|
137
|
+
return (a.createdAt ?? '').localeCompare(b.createdAt ?? '');
|
|
153
138
|
});
|
|
154
139
|
|
|
155
140
|
// Take top 3
|
|
@@ -159,87 +144,68 @@ function computeNextSuggested(session: Session, current: TaskFileExt): string[]
|
|
|
159
144
|
/**
|
|
160
145
|
* Find tasks with blockers in the session scope.
|
|
161
146
|
*/
|
|
162
|
-
function findOpenBlockers(
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
if (!current.tasks) return blockers;
|
|
166
|
-
|
|
167
|
-
const scopeTaskIds = getScopeTaskIds(session, current);
|
|
147
|
+
function findOpenBlockers(tasks: Task[], session: Session): string[] {
|
|
148
|
+
const scopeTaskIds = getScopeTaskIds(session, tasks);
|
|
168
149
|
|
|
169
|
-
|
|
170
|
-
const blockedTasks = current.tasks.filter(
|
|
171
|
-
(t) => scopeTaskIds.has(t.id) && t.status === 'blocked',
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
return blockedTasks.map((t) => t.id);
|
|
150
|
+
return tasks.filter((t) => scopeTaskIds.has(t.id) && t.status === 'blocked').map((t) => t.id);
|
|
175
151
|
}
|
|
176
152
|
|
|
177
153
|
/**
|
|
178
154
|
* Find open bugs in the session scope.
|
|
179
155
|
*/
|
|
180
|
-
function findOpenBugs(
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
t.status !== 'done' &&
|
|
194
|
-
t.status !== 'completed' &&
|
|
195
|
-
t.status !== 'archived' &&
|
|
196
|
-
t.status !== 'cancelled',
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
return bugTasks.map((t) => t.id);
|
|
156
|
+
function findOpenBugs(tasks: Task[], session: Session): string[] {
|
|
157
|
+
const scopeTaskIds = getScopeTaskIds(session, tasks);
|
|
158
|
+
|
|
159
|
+
return tasks
|
|
160
|
+
.filter(
|
|
161
|
+
(t) =>
|
|
162
|
+
scopeTaskIds.has(t.id) &&
|
|
163
|
+
(t.labels ?? []).includes('bug') &&
|
|
164
|
+
t.status !== 'done' &&
|
|
165
|
+
t.status !== 'archived' &&
|
|
166
|
+
t.status !== 'cancelled',
|
|
167
|
+
)
|
|
168
|
+
.map((t) => t.id);
|
|
200
169
|
}
|
|
201
170
|
|
|
202
171
|
/**
|
|
203
172
|
* Get set of task IDs within the session scope.
|
|
204
173
|
*/
|
|
205
|
-
function getScopeTaskIds(session: Session,
|
|
174
|
+
function getScopeTaskIds(session: Session, tasks: Task[]): Set<string> {
|
|
206
175
|
const taskIds = new Set<string>();
|
|
207
176
|
|
|
208
|
-
if (!current.tasks) return taskIds;
|
|
209
|
-
|
|
210
177
|
if (session.scope.type === 'global') {
|
|
211
|
-
|
|
212
|
-
for (const t of current.tasks) {
|
|
178
|
+
for (const t of tasks) {
|
|
213
179
|
taskIds.add(t.id);
|
|
214
180
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return taskIds;
|
|
181
|
+
return taskIds;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Epic/task scope: root task and descendants
|
|
185
|
+
const rootId = session.scope.rootTaskId ?? session.scope.epicId;
|
|
186
|
+
if (!rootId) {
|
|
187
|
+
// No root ID, fall back to global
|
|
188
|
+
for (const t of tasks) {
|
|
189
|
+
taskIds.add(t.id);
|
|
225
190
|
}
|
|
191
|
+
return taskIds;
|
|
192
|
+
}
|
|
226
193
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
194
|
+
const addDescendants = (taskId: string) => {
|
|
195
|
+
taskIds.add(taskId);
|
|
196
|
+
for (const t of tasks) {
|
|
197
|
+
if (t.parentId === taskId) {
|
|
198
|
+
addDescendants(t.id);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
235
202
|
|
|
236
|
-
|
|
203
|
+
addDescendants(rootId);
|
|
237
204
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
205
|
+
// Include explicitTaskIds if present in scope
|
|
206
|
+
if (session.scope.explicitTaskIds) {
|
|
207
|
+
for (const id of session.scope.explicitTaskIds) {
|
|
208
|
+
taskIds.add(id);
|
|
243
209
|
}
|
|
244
210
|
}
|
|
245
211
|
|
package/src/sessions/index.ts
CHANGED
|
@@ -471,4 +471,8 @@ export { getSessionStats } from './session-stats.js';
|
|
|
471
471
|
export { suspendSession } from './session-suspend.js';
|
|
472
472
|
export { switchSession } from './session-switch.js';
|
|
473
473
|
export { SessionView } from './session-view.js';
|
|
474
|
-
export type {
|
|
474
|
+
export type {
|
|
475
|
+
AssumptionRecord,
|
|
476
|
+
DecisionRecord,
|
|
477
|
+
TaskWorkStateExt,
|
|
478
|
+
} from './types.js';
|
package/src/sessions/types.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* @epic T4654
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type { SessionNote, TaskWorkState } from '@cleocode/contracts';
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Session object (engine-compatible).
|
|
10
12
|
*/
|
|
@@ -67,45 +69,22 @@ export interface SessionRecord {
|
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
71
|
* Task work state from the task store.
|
|
72
|
+
*
|
|
73
|
+
* Extends the strict contracts {@link TaskWorkState} with required-null
|
|
74
|
+
* fields for session engine compatibility. The engine layer always expects
|
|
75
|
+
* these fields to be present (even if null), whereas the contracts type
|
|
76
|
+
* marks them as optional.
|
|
70
77
|
*/
|
|
71
|
-
export interface TaskWorkStateExt {
|
|
78
|
+
export interface TaskWorkStateExt extends TaskWorkState {
|
|
72
79
|
currentTask: string | null;
|
|
73
80
|
currentPhase: string | null;
|
|
74
81
|
blockedUntil: string | null;
|
|
75
82
|
sessionNote: string | null;
|
|
76
|
-
sessionNotes:
|
|
83
|
+
sessionNotes: SessionNote[];
|
|
77
84
|
nextAction: string | null;
|
|
78
85
|
primarySession: string | null;
|
|
79
86
|
}
|
|
80
87
|
|
|
81
|
-
/**
|
|
82
|
-
* Task file structure (subset for session operations).
|
|
83
|
-
*/
|
|
84
|
-
export interface TaskFileExt {
|
|
85
|
-
focus?:
|
|
86
|
-
| TaskWorkStateExt
|
|
87
|
-
| { currentTask?: string | null; currentPhase?: string | null; [key: string]: unknown };
|
|
88
|
-
_meta?: {
|
|
89
|
-
schemaVersion: string;
|
|
90
|
-
checksum?: string;
|
|
91
|
-
configVersion?: string;
|
|
92
|
-
lastSessionId?: string | null;
|
|
93
|
-
activeSessionCount?: number;
|
|
94
|
-
sessionsFile?: string | null;
|
|
95
|
-
generation?: number;
|
|
96
|
-
[key: string]: unknown;
|
|
97
|
-
};
|
|
98
|
-
tasks?: Array<{
|
|
99
|
-
id: string;
|
|
100
|
-
status: string;
|
|
101
|
-
parentId?: string;
|
|
102
|
-
completedAt?: string;
|
|
103
|
-
[key: string]: unknown;
|
|
104
|
-
}>;
|
|
105
|
-
lastUpdated?: string;
|
|
106
|
-
[key: string]: unknown;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
88
|
/**
|
|
110
89
|
* Decision record stored in decisions.jsonl.
|
|
111
90
|
*/
|
|
@@ -131,16 +110,3 @@ export interface AssumptionRecord {
|
|
|
131
110
|
validatedAt: string | null;
|
|
132
111
|
timestamp: string;
|
|
133
112
|
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Convert a TaskFile (from contracts) to the looser TaskFileExt shape.
|
|
137
|
-
* Accepts any object with at least the basic TaskFileExt structure.
|
|
138
|
-
* The runtime object is the same reference — this only changes the TS type.
|
|
139
|
-
*/
|
|
140
|
-
export function toTaskFileExt<
|
|
141
|
-
T extends { _meta?: object; tasks?: unknown[]; focus?: object; lastUpdated?: string },
|
|
142
|
-
>(taskFile: T): TaskFileExt {
|
|
143
|
-
// The incoming object structurally satisfies TaskFileExt at runtime;
|
|
144
|
-
// this conversion bridges the strict contracts type to the loose session type.
|
|
145
|
-
return taskFile as TaskFileExt;
|
|
146
|
-
}
|
|
@@ -104,8 +104,11 @@ export class SignalDockTransport implements AgentTransport {
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
async poll(agentId: string,
|
|
108
|
-
const
|
|
107
|
+
async poll(agentId: string, since?: string): Promise<Message[]> {
|
|
108
|
+
const path = since
|
|
109
|
+
? `/messages/poll/new?since=${encodeURIComponent(since)}`
|
|
110
|
+
: '/messages/poll/new';
|
|
111
|
+
const messages = await this.request<Message[]>('GET', path, undefined, agentId);
|
|
109
112
|
return messages;
|
|
110
113
|
}
|
|
111
114
|
|
|
@@ -19,10 +19,9 @@
|
|
|
19
19
|
|
|
20
20
|
import { existsSync, readFileSync } from 'node:fs';
|
|
21
21
|
import { join } from 'node:path';
|
|
22
|
-
import type { Task } from '@cleocode/contracts';
|
|
23
22
|
import { ExitCode } from '@cleocode/contracts';
|
|
24
23
|
import { CleoError } from '../../errors.js';
|
|
25
|
-
import { getProjectRoot
|
|
24
|
+
import { getProjectRoot } from '../../paths.js';
|
|
26
25
|
import { findSkill } from '../discovery.js';
|
|
27
26
|
import { injectTokens, type TokenValues } from './token.js';
|
|
28
27
|
|
|
@@ -55,15 +54,10 @@ export function loadProtocolBase(cwd?: string): string | null {
|
|
|
55
54
|
* Build task context block for injection into a subagent prompt.
|
|
56
55
|
* @task T4521
|
|
57
56
|
*/
|
|
58
|
-
export function buildTaskContext(taskId: string, cwd?: string): string {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const data = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
65
|
-
const tasks: Task[] = data.tasks ?? [];
|
|
66
|
-
const task = tasks.find((t) => t.id === taskId);
|
|
57
|
+
export async function buildTaskContext(taskId: string, cwd?: string): Promise<string> {
|
|
58
|
+
const { getAccessor } = await import('../../store/data-accessor.js');
|
|
59
|
+
const acc = await getAccessor(cwd);
|
|
60
|
+
const task = await acc.loadSingleTask(taskId);
|
|
67
61
|
|
|
68
62
|
if (!task) {
|
|
69
63
|
return `## Task Context\n\n**Task**: ${taskId}\n**Status**: not found\n`;
|
|
@@ -165,15 +159,15 @@ export function filterProtocolByTier(content: string, tier: 0 | 1 | 2): string {
|
|
|
165
159
|
* Composes: skill content + protocol base + task context.
|
|
166
160
|
* @task T4521
|
|
167
161
|
*/
|
|
168
|
-
export function injectProtocol(
|
|
162
|
+
export async function injectProtocol(
|
|
169
163
|
skillContent: string,
|
|
170
164
|
taskId: string,
|
|
171
165
|
tokenValues: TokenValues,
|
|
172
166
|
cwd?: string,
|
|
173
167
|
tier?: 0 | 1 | 2,
|
|
174
|
-
): string {
|
|
168
|
+
): Promise<string> {
|
|
175
169
|
const protocolBase = loadProtocolBase(cwd);
|
|
176
|
-
const taskContext = buildTaskContext(taskId, cwd);
|
|
170
|
+
const taskContext = await buildTaskContext(taskId, cwd);
|
|
177
171
|
|
|
178
172
|
// Inject tokens into skill content
|
|
179
173
|
const resolvedSkill = injectTokens(skillContent, tokenValues);
|
|
@@ -202,13 +196,13 @@ export function injectProtocol(
|
|
|
202
196
|
* High-level function that loads the skill, injects protocol, and returns the prompt.
|
|
203
197
|
* @task T4521
|
|
204
198
|
*/
|
|
205
|
-
export function orchestratorSpawnSkill(
|
|
199
|
+
export async function orchestratorSpawnSkill(
|
|
206
200
|
taskId: string,
|
|
207
201
|
skillName: string,
|
|
208
202
|
tokenValues: TokenValues,
|
|
209
203
|
cwd?: string,
|
|
210
204
|
tier?: 0 | 1 | 2,
|
|
211
|
-
): string {
|
|
205
|
+
): Promise<string> {
|
|
212
206
|
// Find the skill
|
|
213
207
|
const skill = findSkill(skillName, cwd);
|
|
214
208
|
if (!skill || !skill.content) {
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { randomBytes } from 'node:crypto';
|
|
14
14
|
import { existsSync, readFileSync } from 'node:fs';
|
|
15
|
-
import
|
|
16
|
-
import { getTaskPath } from '../../paths.js';
|
|
15
|
+
import { getAccessor } from '../../store/data-accessor.js';
|
|
17
16
|
import type { ManifestEntry } from '../types.js';
|
|
18
17
|
|
|
19
18
|
// ============================================================================
|
|
@@ -65,20 +64,13 @@ export function generateContributionId(taskId: string): string {
|
|
|
65
64
|
* Validate that a task is suitable for contribution protocol.
|
|
66
65
|
* @task T4520
|
|
67
66
|
*/
|
|
68
|
-
export function validateContributionTask(
|
|
67
|
+
export async function validateContributionTask(
|
|
69
68
|
taskId: string,
|
|
70
69
|
cwd?: string,
|
|
71
|
-
): { valid: boolean; issues: string[] } {
|
|
70
|
+
): Promise<{ valid: boolean; issues: string[] }> {
|
|
72
71
|
const issues: string[] = [];
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
if (!existsSync(taskPath)) {
|
|
76
|
-
return { valid: false, issues: ['Todo file not found'] };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const data = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
80
|
-
const tasks: Task[] = data.tasks ?? [];
|
|
81
|
-
const task = tasks.find((t) => t.id === taskId);
|
|
72
|
+
const acc = await getAccessor(cwd);
|
|
73
|
+
const task = await acc.loadSingleTask(taskId);
|
|
82
74
|
|
|
83
75
|
if (!task) {
|
|
84
76
|
return { valid: false, issues: [`Task ${taskId} not found`] };
|