@grackle-ai/server 0.41.1 → 0.43.0
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/db.d.ts.map +1 -1
- package/dist/db.js +55 -1
- package/dist/db.js.map +1 -1
- package/dist/event-processor.js +4 -4
- package/dist/event-processor.js.map +1 -1
- package/dist/grpc-service.d.ts.map +1 -1
- package/dist/grpc-service.js +43 -36
- package/dist/grpc-service.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/log-writer.d.ts +8 -0
- package/dist/log-writer.d.ts.map +1 -1
- package/dist/log-writer.js +12 -0
- package/dist/log-writer.js.map +1 -1
- package/dist/processor-registry.d.ts +2 -2
- package/dist/processor-registry.d.ts.map +1 -1
- package/dist/processor-registry.js.map +1 -1
- package/dist/reanimate-agent.js +1 -1
- package/dist/reanimate-agent.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +0 -1
- package/dist/schema.js.map +1 -1
- package/dist/settings-store.d.ts.map +1 -1
- package/dist/settings-store.js +1 -0
- package/dist/settings-store.js.map +1 -1
- package/dist/signals/sigchld.d.ts +7 -0
- package/dist/signals/sigchld.d.ts.map +1 -0
- package/dist/signals/sigchld.js +163 -0
- package/dist/signals/sigchld.js.map +1 -0
- package/dist/signals/signal-delivery.d.ts +14 -0
- package/dist/signals/signal-delivery.d.ts.map +1 -0
- package/dist/signals/signal-delivery.js +167 -0
- package/dist/signals/signal-delivery.js.map +1 -0
- package/dist/task-store.d.ts +5 -5
- package/dist/task-store.d.ts.map +1 -1
- package/dist/task-store.js +24 -12
- package/dist/task-store.js.map +1 -1
- package/dist/ws-bridge.js +40 -36
- package/dist/ws-bridge.js.map +1 -1
- package/package.json +9 -7
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { create } from "@bufbuild/protobuf";
|
|
2
|
+
import { grackle, powerline, SESSION_STATUS } from "@grackle-ai/common";
|
|
3
|
+
import * as sessionStore from "../session-store.js";
|
|
4
|
+
import * as adapterManager from "../adapter-manager.js";
|
|
5
|
+
import { reanimateAgent } from "../reanimate-agent.js";
|
|
6
|
+
import * as streamHub from "../stream-hub.js";
|
|
7
|
+
import * as logWriter from "../log-writer.js";
|
|
8
|
+
import { logger } from "../logger.js";
|
|
9
|
+
/** Timeout (ms) to wait for a reanimated session to reach IDLE. */
|
|
10
|
+
const REANIMATE_IDLE_TIMEOUT_MS = 60_000;
|
|
11
|
+
/** Statuses considered active (the agent can accept input). */
|
|
12
|
+
const ACTIVE_STATUSES = new Set([
|
|
13
|
+
SESSION_STATUS.IDLE,
|
|
14
|
+
SESSION_STATUS.RUNNING,
|
|
15
|
+
SESSION_STATUS.PENDING,
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Deliver a signal message to a task's agent session.
|
|
19
|
+
*
|
|
20
|
+
* Active session (IDLE/RUNNING/PENDING) → sendInput bypassing the IDLE guard.
|
|
21
|
+
* Dead session (COMPLETED/FAILED/INTERRUPTED) → reanimate + wait for IDLE + sendInput.
|
|
22
|
+
* No session at all → log warning, return false.
|
|
23
|
+
*
|
|
24
|
+
* @param taskId - Target task whose session should receive the signal.
|
|
25
|
+
* @param signalType - Signal kind for logging (e.g. "sigchld").
|
|
26
|
+
* @param message - The text content to deliver as user input.
|
|
27
|
+
* @returns true if the message was sent, false otherwise.
|
|
28
|
+
*/
|
|
29
|
+
export async function deliverSignalToTask(taskId, signalType, message) {
|
|
30
|
+
// ── 1. Try an active session ──────────────────────────────
|
|
31
|
+
const activeSessions = sessionStore.getActiveSessionsForTask(taskId);
|
|
32
|
+
if (activeSessions.length > 0) {
|
|
33
|
+
const session = activeSessions[0];
|
|
34
|
+
return await sendInputToSession(session.id, session.environmentId, message, signalType);
|
|
35
|
+
}
|
|
36
|
+
// ── 2. Try reanimating the latest terminal session ────────
|
|
37
|
+
const latest = sessionStore.getLatestSessionForTask(taskId);
|
|
38
|
+
if (!latest) {
|
|
39
|
+
logger.warn({ taskId, signalType }, "No session exists for task — signal dropped");
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (ACTIVE_STATUSES.has(latest.status)) {
|
|
43
|
+
// Should not happen (getActiveSessionsForTask would have found it),
|
|
44
|
+
// but handle it defensively.
|
|
45
|
+
return await sendInputToSession(latest.id, latest.environmentId, message, signalType);
|
|
46
|
+
}
|
|
47
|
+
if (!latest.runtimeSessionId) {
|
|
48
|
+
logger.warn({ taskId, sessionId: latest.id, signalType }, "Latest session has no runtimeSessionId — cannot reanimate, signal dropped");
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
// Subscribe to the session stream BEFORE reanimating so we don't miss
|
|
52
|
+
// the waiting_input event if the runtime reaches IDLE quickly (e.g. stub).
|
|
53
|
+
const idleWaiter = waitForSessionIdle(latest.id, REANIMATE_IDLE_TIMEOUT_MS);
|
|
54
|
+
try {
|
|
55
|
+
reanimateAgent(latest.id);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
idleWaiter.cancel();
|
|
59
|
+
logger.error({ err, taskId, sessionId: latest.id, signalType }, "Failed to reanimate session for signal delivery");
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
// Wait for the reanimated session to reach IDLE
|
|
63
|
+
const reachedIdle = await idleWaiter.promise;
|
|
64
|
+
if (!reachedIdle) {
|
|
65
|
+
logger.error({ taskId, sessionId: latest.id, signalType }, "Reanimated session did not reach IDLE within timeout — signal dropped");
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return await sendInputToSession(latest.id, latest.environmentId, message, signalType);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Send input text to a session via its environment's PowerLine connection.
|
|
72
|
+
* Bypasses the server-side IDLE guard — the agent runtime accepts input at any
|
|
73
|
+
* time and picks it up at the next turn boundary.
|
|
74
|
+
*/
|
|
75
|
+
async function sendInputToSession(sessionId, environmentId, text, signalType) {
|
|
76
|
+
const conn = adapterManager.getConnection(environmentId);
|
|
77
|
+
if (!conn) {
|
|
78
|
+
logger.error({ sessionId, environmentId, signalType }, "Environment not connected — signal delivery failed");
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
// Record the signal as a user_input event in the session log and stream,
|
|
83
|
+
// matching the pattern used by the WS bridge for regular user input.
|
|
84
|
+
const session = sessionStore.getSession(sessionId);
|
|
85
|
+
const userInputEvent = create(grackle.SessionEventSchema, {
|
|
86
|
+
sessionId,
|
|
87
|
+
type: grackle.EventType.USER_INPUT,
|
|
88
|
+
timestamp: new Date().toISOString(),
|
|
89
|
+
content: text,
|
|
90
|
+
});
|
|
91
|
+
if (session?.logPath) {
|
|
92
|
+
// Ensure the stream is open before writing — the session may still be
|
|
93
|
+
// PENDING and processEventStream may not have called initLog yet.
|
|
94
|
+
logWriter.ensureLogInitialized(session.logPath);
|
|
95
|
+
logWriter.writeEvent(session.logPath, userInputEvent);
|
|
96
|
+
}
|
|
97
|
+
streamHub.publish(userInputEvent);
|
|
98
|
+
await conn.client.sendInput(create(powerline.InputMessageSchema, { sessionId, text }));
|
|
99
|
+
logger.info({ sessionId, signalType }, "Signal delivered to session");
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
logger.error({ err, sessionId, signalType }, "sendInput failed during signal delivery");
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Begin watching for a session to reach IDLE status via StreamHub events.
|
|
109
|
+
* Subscribes immediately so events are captured even before the caller starts
|
|
110
|
+
* the session (e.g. via reanimateAgent).
|
|
111
|
+
*/
|
|
112
|
+
function waitForSessionIdle(sessionId, timeoutMs) {
|
|
113
|
+
const stream = streamHub.createStream(sessionId);
|
|
114
|
+
let timer;
|
|
115
|
+
const promise = (async () => {
|
|
116
|
+
// Check DB after subscribing to close the race window.
|
|
117
|
+
const current = sessionStore.getSession(sessionId);
|
|
118
|
+
if (current?.status === SESSION_STATUS.IDLE) {
|
|
119
|
+
stream.cancel();
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
return await Promise.race([
|
|
124
|
+
(async () => {
|
|
125
|
+
for await (const event of stream) {
|
|
126
|
+
// Only inspect status events — other event types (text, tool_use, etc.)
|
|
127
|
+
// can have arbitrary content that might accidentally match status strings.
|
|
128
|
+
if (event.type !== grackle.EventType.STATUS) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
// "waiting_input" is the runtime status that maps to IDLE in the session store.
|
|
132
|
+
if (event.content === "waiting_input") {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
// If the session hit a terminal state, stop waiting
|
|
136
|
+
if (["completed", "failed", "killed", "interrupted"].includes(event.content)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
})(),
|
|
142
|
+
new Promise((resolve) => {
|
|
143
|
+
timer = setTimeout(() => {
|
|
144
|
+
stream.cancel();
|
|
145
|
+
resolve(false);
|
|
146
|
+
}, timeoutMs);
|
|
147
|
+
}),
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
if (timer !== undefined) {
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
}
|
|
154
|
+
stream.cancel();
|
|
155
|
+
}
|
|
156
|
+
})();
|
|
157
|
+
return {
|
|
158
|
+
promise,
|
|
159
|
+
cancel: () => {
|
|
160
|
+
stream.cancel();
|
|
161
|
+
if (timer !== undefined) {
|
|
162
|
+
clearTimeout(timer);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=signal-delivery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-delivery.js","sourceRoot":"","sources":["../../src/signals/signal-delivery.ts"],"names":[],"mappings":"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"}
|
package/dist/task-store.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type TaskRow } from "./schema.js";
|
|
|
2
2
|
import type { TaskStatus } from "@grackle-ai/common";
|
|
3
3
|
export type { TaskRow };
|
|
4
4
|
/** Insert a new task with auto-generated branch name and sort order. */
|
|
5
|
-
export declare function createTask(id: string, projectId: string, title: string, description: string, dependsOn: string[], projectSlug: string, parentTaskId?: string, canDecompose?: boolean, defaultPersonaId?: string): void;
|
|
5
|
+
export declare function createTask(id: string, projectId: string | undefined, title: string, description: string, dependsOn: string[], projectSlug: string, parentTaskId?: string, canDecompose?: boolean, defaultPersonaId?: string): void;
|
|
6
6
|
/** Retrieve a single task by ID. */
|
|
7
7
|
export declare function getTask(id: string): TaskRow | undefined;
|
|
8
8
|
/** Options for filtering the task list. */
|
|
@@ -12,8 +12,8 @@ export interface ListTasksOptions {
|
|
|
12
12
|
/** Exact match filter on task status (e.g. "not_started", "in_progress"). */
|
|
13
13
|
status?: string;
|
|
14
14
|
}
|
|
15
|
-
/** Return tasks for a project, with optional search/status filters, ordered by sort_order then created_at. */
|
|
16
|
-
export declare function listTasks(projectId
|
|
15
|
+
/** Return tasks for a project (or all tasks when projectId is omitted), with optional search/status filters, ordered by sort_order then created_at. */
|
|
16
|
+
export declare function listTasks(projectId?: string, options?: ListTasksOptions): TaskRow[];
|
|
17
17
|
/** Update multiple task fields at once. */
|
|
18
18
|
export declare function updateTask(id: string, title: string, description: string, status: string, dependsOn: string[], defaultPersonaId?: string): void;
|
|
19
19
|
/** Update only the dependsOn array of a task. */
|
|
@@ -28,9 +28,9 @@ export declare function markTaskComplete(id: string, status?: "complete" | "fail
|
|
|
28
28
|
/** Delete a task by ID. Returns the number of rows affected. */
|
|
29
29
|
export declare function deleteTask(id: string): number;
|
|
30
30
|
/** Return all not_started tasks whose dependencies are fully met. */
|
|
31
|
-
export declare function getUnblockedTasks(projectId
|
|
31
|
+
export declare function getUnblockedTasks(projectId?: string): TaskRow[];
|
|
32
32
|
/** Alias for getUnblockedTasks — check which pending tasks are now unblocked. */
|
|
33
|
-
export declare function checkAndUnblock(projectId
|
|
33
|
+
export declare function checkAndUnblock(projectId?: string): TaskRow[];
|
|
34
34
|
/** Check whether all dependencies of a task are in "complete" status. */
|
|
35
35
|
export declare function areDependenciesMet(taskId: string): boolean;
|
|
36
36
|
/** Build a map from parentTaskId to child IDs from a pre-fetched list of rows. Avoids N+1 queries. */
|
package/dist/task-store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../src/task-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKrD,YAAY,EAAE,OAAO,EAAE,CAAC;AAExB,wEAAwE;AACxE,wBAAgB,UAAU,CACxB,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../src/task-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKrD,YAAY,EAAE,OAAO,EAAE,CAAC;AAExB,wEAAwE;AACxE,wBAAgB,UAAU,CACxB,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,MAAW,EACzB,YAAY,CAAC,EAAE,OAAO,EACtB,gBAAgB,GAAE,MAAW,GAC5B,IAAI,CAsDN;AAED,oCAAoC;AACpC,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAEvD;AAED,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,mHAAmH;IACnH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,uJAAuJ;AACvJ,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,EAAE,CAqCnF;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,CACxB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EAAE,EACnB,gBAAgB,CAAC,EAAE,MAAM,GACxB,IAAI,CAeN;AAED,iDAAiD;AACjD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAQtE;AAED,mCAAmC;AACnC,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAQrE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,MAAM,GAAE,UAAU,GAAG,QAAqB,GACzC,IAAI,CASN;AAED,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAe/D;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAE7D;AAED,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAa1D;AAID,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAavE;AAED,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAOrD;AAED,2HAA2H;AAC3H,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CA0BxD;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAYtD;AAED,kDAAkD;AAClD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO3E"}
|
package/dist/task-store.js
CHANGED
|
@@ -24,21 +24,27 @@ export function createTask(id, projectId, title, description, dependsOn, project
|
|
|
24
24
|
branch = `${parent.branch}/${slugify(title)}`;
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
|
-
|
|
27
|
+
const prefix = projectSlug || "task";
|
|
28
|
+
branch = `${prefix}/${slugify(title)}`;
|
|
28
29
|
}
|
|
29
30
|
// Derive canDecompose when not explicitly set: root=true, child=false
|
|
30
31
|
const resolvedCanDecompose = canDecompose ?? !parentTaskId;
|
|
31
32
|
const depsJson = JSON.stringify(dependsOn);
|
|
32
|
-
const
|
|
33
|
+
const sortOrderConditions = [];
|
|
34
|
+
if (projectId) {
|
|
35
|
+
sortOrderConditions.push(eq(tasks.projectId, projectId));
|
|
36
|
+
}
|
|
37
|
+
const maxRowQuery = db
|
|
33
38
|
.select({ maxOrder: sql `max(sort_order)` })
|
|
34
|
-
.from(tasks)
|
|
35
|
-
|
|
36
|
-
.get()
|
|
39
|
+
.from(tasks);
|
|
40
|
+
const maxRow = sortOrderConditions.length > 0
|
|
41
|
+
? maxRowQuery.where(and(...sortOrderConditions)).get()
|
|
42
|
+
: maxRowQuery.get();
|
|
37
43
|
const sortOrder = (maxRow?.maxOrder ?? -1) + 1;
|
|
38
44
|
db.insert(tasks)
|
|
39
45
|
.values({
|
|
40
46
|
id,
|
|
41
|
-
projectId,
|
|
47
|
+
projectId: projectId || null,
|
|
42
48
|
title,
|
|
43
49
|
description,
|
|
44
50
|
branch,
|
|
@@ -59,9 +65,12 @@ export function getTask(id) {
|
|
|
59
65
|
function escapeLikePattern(value) {
|
|
60
66
|
return value.replace(/[%_\\]/g, (ch) => `\\${ch}`);
|
|
61
67
|
}
|
|
62
|
-
/** Return tasks for a project, with optional search/status filters, ordered by sort_order then created_at. */
|
|
68
|
+
/** Return tasks for a project (or all tasks when projectId is omitted), with optional search/status filters, ordered by sort_order then created_at. */
|
|
63
69
|
export function listTasks(projectId, options) {
|
|
64
|
-
const conditions = [
|
|
70
|
+
const conditions = [];
|
|
71
|
+
if (projectId) {
|
|
72
|
+
conditions.push(eq(tasks.projectId, projectId));
|
|
73
|
+
}
|
|
65
74
|
if (options?.status) {
|
|
66
75
|
// Normalize legacy status aliases (e.g. "in_progress" → "working")
|
|
67
76
|
const canonical = taskStatusToString(taskStatusToEnum(options.status));
|
|
@@ -78,10 +87,13 @@ export function listTasks(projectId, options) {
|
|
|
78
87
|
const pattern = `%${escaped}%`;
|
|
79
88
|
conditions.push(or(sql `${tasks.title} LIKE ${pattern} ESCAPE '\\'`, sql `${tasks.description} LIKE ${pattern} ESCAPE '\\'`));
|
|
80
89
|
}
|
|
81
|
-
|
|
90
|
+
const query = db
|
|
82
91
|
.select()
|
|
83
|
-
.from(tasks)
|
|
84
|
-
|
|
92
|
+
.from(tasks);
|
|
93
|
+
const filtered = conditions.length > 0
|
|
94
|
+
? query.where(and(...conditions))
|
|
95
|
+
: query;
|
|
96
|
+
return filtered
|
|
85
97
|
.orderBy(asc(tasks.sortOrder), asc(tasks.createdAt))
|
|
86
98
|
.all();
|
|
87
99
|
}
|
|
@@ -209,7 +221,7 @@ export function getDescendants(taskId) {
|
|
|
209
221
|
if (!task) {
|
|
210
222
|
return [];
|
|
211
223
|
}
|
|
212
|
-
const allRows = listTasks(task.projectId);
|
|
224
|
+
const allRows = listTasks(task.projectId || undefined);
|
|
213
225
|
const childIdsMap = buildChildIdsMap(allRows);
|
|
214
226
|
const rowById = new Map(allRows.map((r) => [r.id, r]));
|
|
215
227
|
const result = [];
|
package/dist/task-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-store.js","sourceRoot":"","sources":["../src/task-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,KAAK,EAAgB,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"task-store.js","sourceRoot":"","sources":["../src/task-store.ts"],"names":[],"mappings":"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"}
|
package/dist/ws-bridge.js
CHANGED
|
@@ -174,12 +174,12 @@ async function autoProvisionEnvironment(ws, environmentId, env, logContext) {
|
|
|
174
174
|
* string for failures that need the caller to surface to the client.
|
|
175
175
|
*/
|
|
176
176
|
async function startTaskSession(ws, task, options) {
|
|
177
|
-
const project = projectStore.getProject(task.projectId);
|
|
178
|
-
if (!project) {
|
|
177
|
+
const project = task.projectId ? projectStore.getProject(task.projectId) : undefined;
|
|
178
|
+
if (task.projectId && !project) {
|
|
179
179
|
logger.warn({ taskId: task.id }, "startTaskSession failed: project not found");
|
|
180
180
|
return `Project not found: ${task.projectId}`;
|
|
181
181
|
}
|
|
182
|
-
const environmentId = options?.environmentId || project
|
|
182
|
+
const environmentId = options?.environmentId || project?.defaultEnvironmentId || "";
|
|
183
183
|
const env = envRegistry.getEnvironment(environmentId);
|
|
184
184
|
if (!env) {
|
|
185
185
|
logger.warn({ taskId: task.id, environmentId }, "startTaskSession failed: environment not found");
|
|
@@ -194,7 +194,7 @@ async function startTaskSession(ws, task, options) {
|
|
|
194
194
|
// Resolve persona via cascade (request → task → project → app default)
|
|
195
195
|
let resolved;
|
|
196
196
|
try {
|
|
197
|
-
resolved = resolvePersona(options?.personaId || "", task.defaultPersonaId, project
|
|
197
|
+
resolved = resolvePersona(options?.personaId || "", task.defaultPersonaId, project?.defaultPersonaId || "");
|
|
198
198
|
}
|
|
199
199
|
catch (err) {
|
|
200
200
|
return err.message;
|
|
@@ -211,7 +211,7 @@ async function startTaskSession(ws, task, options) {
|
|
|
211
211
|
emit("task.started", {
|
|
212
212
|
taskId: freshTask.id,
|
|
213
213
|
sessionId,
|
|
214
|
-
projectId: freshTask.projectId,
|
|
214
|
+
projectId: freshTask.projectId || "",
|
|
215
215
|
});
|
|
216
216
|
// Re-push stored tokens + provider credentials (scoped to runtime) so they're fresh for this session.
|
|
217
217
|
// For local envs, skip file tokens — the PowerLine is on the same machine.
|
|
@@ -233,7 +233,8 @@ async function startTaskSession(ws, task, options) {
|
|
|
233
233
|
const mcpPort = parseInt(process.env.GRACKLE_MCP_PORT || String(DEFAULT_MCP_PORT), 10);
|
|
234
234
|
const mcpDialHost = toDialableHost(process.env.GRACKLE_HOST || "127.0.0.1");
|
|
235
235
|
const mcpUrl = `http://${mcpDialHost}:${mcpPort}/mcp`;
|
|
236
|
-
const mcpToken = createScopedToken({ sub: freshTask.id, pid: freshTask.projectId, per: resolved.personaId, sid: sessionId }, loadOrCreateApiKey());
|
|
236
|
+
const mcpToken = createScopedToken({ sub: freshTask.id, pid: freshTask.projectId || "", per: resolved.personaId, sid: sessionId }, loadOrCreateApiKey());
|
|
237
|
+
const useWorktrees = project?.useWorktrees ?? false;
|
|
237
238
|
const powerlineReq = create(powerline.SpawnRequestSchema, {
|
|
238
239
|
sessionId,
|
|
239
240
|
runtime,
|
|
@@ -241,11 +242,11 @@ async function startTaskSession(ws, task, options) {
|
|
|
241
242
|
model,
|
|
242
243
|
maxTurns,
|
|
243
244
|
branch: freshTask.branch,
|
|
244
|
-
worktreeBasePath: freshTask.branch
|
|
245
|
-
? (project
|
|
245
|
+
worktreeBasePath: freshTask.branch && useWorktrees
|
|
246
|
+
? (project?.worktreeBasePath || process.env.GRACKLE_WORKTREE_BASE || "/workspace")
|
|
246
247
|
: "",
|
|
247
248
|
systemContext,
|
|
248
|
-
projectId: freshTask.projectId,
|
|
249
|
+
projectId: freshTask.projectId ?? undefined,
|
|
249
250
|
taskId: freshTask.id,
|
|
250
251
|
mcpServersJson,
|
|
251
252
|
mcpUrl,
|
|
@@ -254,7 +255,7 @@ async function startTaskSession(ws, task, options) {
|
|
|
254
255
|
processEventStream(conn.client.spawn(powerlineReq), {
|
|
255
256
|
sessionId,
|
|
256
257
|
logPath,
|
|
257
|
-
projectId: freshTask.projectId,
|
|
258
|
+
projectId: freshTask.projectId ?? undefined,
|
|
258
259
|
taskId: freshTask.id,
|
|
259
260
|
});
|
|
260
261
|
return undefined;
|
|
@@ -529,7 +530,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
529
530
|
if (session.taskId) {
|
|
530
531
|
const task = taskStore.getTask(session.taskId);
|
|
531
532
|
if (task) {
|
|
532
|
-
emit("task.updated", { taskId: task.id, projectId: task.projectId });
|
|
533
|
+
emit("task.updated", { taskId: task.id, projectId: task.projectId || "" });
|
|
533
534
|
}
|
|
534
535
|
}
|
|
535
536
|
break;
|
|
@@ -716,7 +717,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
716
717
|
});
|
|
717
718
|
return;
|
|
718
719
|
}
|
|
719
|
-
personaStore.updatePersona(updatePersonaId, msg.payload?.name
|
|
720
|
+
personaStore.updatePersona(updatePersonaId, msg.payload?.name ?? existingPersona.name, msg.payload?.description ?? existingPersona.description, msg.payload?.systemPrompt ?? existingPersona.systemPrompt, msg.payload?.toolConfig ?? existingPersona.toolConfig, msg.payload?.runtime ?? existingPersona.runtime, msg.payload?.model ?? existingPersona.model, msg.payload?.maxTurns ?? existingPersona.maxTurns, msg.payload?.mcpServers ?? existingPersona.mcpServers);
|
|
720
721
|
emit("persona.updated", { personaId: updatePersonaId });
|
|
721
722
|
break;
|
|
722
723
|
}
|
|
@@ -768,9 +769,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
768
769
|
}
|
|
769
770
|
// ─── Tasks ─────────────────────────────────────────────
|
|
770
771
|
case "list_tasks": {
|
|
771
|
-
const projectId = msg.payload?.projectId;
|
|
772
|
-
if (!projectId)
|
|
773
|
-
return;
|
|
772
|
+
const projectId = msg.payload?.projectId || undefined;
|
|
774
773
|
const rows = taskStore.listTasks(projectId, {
|
|
775
774
|
search: msg.payload?.search || undefined,
|
|
776
775
|
status: msg.payload?.status || undefined,
|
|
@@ -794,7 +793,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
794
793
|
const computed = computeTaskStatus(r.status, taskSessions);
|
|
795
794
|
return {
|
|
796
795
|
id: r.id,
|
|
797
|
-
projectId: r.projectId,
|
|
796
|
+
projectId: r.projectId ?? undefined,
|
|
798
797
|
title: r.title,
|
|
799
798
|
description: r.description,
|
|
800
799
|
status: computed.status,
|
|
@@ -815,25 +814,28 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
815
814
|
break;
|
|
816
815
|
}
|
|
817
816
|
case "create_task": {
|
|
818
|
-
const projectId = msg.payload?.projectId;
|
|
817
|
+
const projectId = msg.payload?.projectId || undefined;
|
|
819
818
|
const title = msg.payload?.title;
|
|
820
819
|
const requestId = typeof msg.payload?.requestId === "string"
|
|
821
820
|
? msg.payload.requestId
|
|
822
821
|
: "";
|
|
823
|
-
if (!
|
|
822
|
+
if (!title) {
|
|
824
823
|
sendWs(ws, {
|
|
825
824
|
type: "create_task_error",
|
|
826
|
-
payload: { message: "
|
|
825
|
+
payload: { message: "title required", requestId },
|
|
827
826
|
});
|
|
828
827
|
return;
|
|
829
828
|
}
|
|
830
|
-
|
|
831
|
-
if (
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
829
|
+
let project;
|
|
830
|
+
if (projectId) {
|
|
831
|
+
project = projectStore.getProject(projectId);
|
|
832
|
+
if (!project) {
|
|
833
|
+
sendWs(ws, {
|
|
834
|
+
type: "create_task_error",
|
|
835
|
+
payload: { message: `Project not found: ${projectId}`, requestId },
|
|
836
|
+
});
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
837
839
|
}
|
|
838
840
|
const parentTaskId = msg.payload?.parentTaskId || "";
|
|
839
841
|
const rawCanDecompose = msg.payload?.canDecompose;
|
|
@@ -842,7 +844,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
842
844
|
const canDecompose = typeof rawCanDecompose === "boolean" ? rawCanDecompose : false;
|
|
843
845
|
try {
|
|
844
846
|
const id = uuid().slice(0, 8);
|
|
845
|
-
taskStore.createTask(id, projectId, title, msg.payload?.description || "", msg.payload?.dependsOn || [], slugify(project.name), parentTaskId, canDecompose, msg.payload?.defaultPersonaId || "");
|
|
847
|
+
taskStore.createTask(id, projectId, title, msg.payload?.description || "", msg.payload?.dependsOn || [], project ? slugify(project.name) : "", parentTaskId, canDecompose, msg.payload?.defaultPersonaId || "");
|
|
846
848
|
emit("task.created", { taskId: id, projectId, requestId });
|
|
847
849
|
}
|
|
848
850
|
catch (error) {
|
|
@@ -891,13 +893,13 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
891
893
|
}
|
|
892
894
|
sessionStore.setSessionTask(lateBindSessionId, updateTaskId);
|
|
893
895
|
try {
|
|
894
|
-
processorRegistry.lateBind(lateBindSessionId, updateTaskId, existingTask.projectId);
|
|
896
|
+
processorRegistry.lateBind(lateBindSessionId, updateTaskId, existingTask.projectId || undefined);
|
|
895
897
|
}
|
|
896
898
|
catch (err) {
|
|
897
899
|
sendWs(ws, { type: "error", payload: { message: String(err) } });
|
|
898
900
|
return;
|
|
899
901
|
}
|
|
900
|
-
emit("task.started", { taskId: updateTaskId, sessionId: lateBindSessionId, projectId: existingTask.projectId });
|
|
902
|
+
emit("task.started", { taskId: updateTaskId, sessionId: lateBindSessionId, projectId: existingTask.projectId || "" });
|
|
901
903
|
break;
|
|
902
904
|
}
|
|
903
905
|
// Only allow editing not_started tasks (non-late-bind path)
|
|
@@ -926,7 +928,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
926
928
|
? msg.payload.defaultPersonaId
|
|
927
929
|
: undefined;
|
|
928
930
|
taskStore.updateTask(updateTaskId, updatedTitle, updatedDescription, existingTask.status, updatedDependsOn, updatedDefaultPersonaId);
|
|
929
|
-
emit("task.updated", { taskId: updateTaskId, projectId: existingTask.projectId });
|
|
931
|
+
emit("task.updated", { taskId: updateTaskId, projectId: existingTask.projectId || "" });
|
|
930
932
|
break;
|
|
931
933
|
}
|
|
932
934
|
case "start_task": {
|
|
@@ -977,7 +979,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
977
979
|
return;
|
|
978
980
|
taskStore.markTaskComplete(taskId, TASK_STATUS.COMPLETE);
|
|
979
981
|
const task = taskStore.getTask(taskId);
|
|
980
|
-
const unblocked = task ? taskStore.checkAndUnblock(task.projectId) : [];
|
|
982
|
+
const unblocked = task?.projectId ? taskStore.checkAndUnblock(task.projectId) : [];
|
|
981
983
|
sendWs(ws, {
|
|
982
984
|
type: "task_completed",
|
|
983
985
|
payload: {
|
|
@@ -986,7 +988,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
986
988
|
},
|
|
987
989
|
});
|
|
988
990
|
if (task) {
|
|
989
|
-
emit("task.completed", { taskId, projectId: task.projectId });
|
|
991
|
+
emit("task.completed", { taskId, projectId: task.projectId || "" });
|
|
990
992
|
}
|
|
991
993
|
break;
|
|
992
994
|
}
|
|
@@ -1036,10 +1038,10 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
1036
1038
|
processEventStream(conn.client.resume(powerlineReq), {
|
|
1037
1039
|
sessionId: latestSession.id,
|
|
1038
1040
|
logPath,
|
|
1039
|
-
projectId: task.projectId,
|
|
1041
|
+
projectId: task.projectId ?? undefined,
|
|
1040
1042
|
taskId: task.id,
|
|
1041
1043
|
});
|
|
1042
|
-
emit("task.started", { taskId: task.id, sessionId: latestSession.id, projectId: task.projectId });
|
|
1044
|
+
emit("task.started", { taskId: task.id, sessionId: latestSession.id, projectId: task.projectId || "" });
|
|
1043
1045
|
break;
|
|
1044
1046
|
}
|
|
1045
1047
|
case "delete_task": {
|
|
@@ -1088,7 +1090,7 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
1088
1090
|
sendWs(ws, { type: "error", payload: { message: `Failed to delete task ${taskId}: no rows affected` } });
|
|
1089
1091
|
return;
|
|
1090
1092
|
}
|
|
1091
|
-
emit("task.deleted", { taskId, projectId: deletedTask.projectId });
|
|
1093
|
+
emit("task.deleted", { taskId, projectId: deletedTask.projectId || "" });
|
|
1092
1094
|
break;
|
|
1093
1095
|
}
|
|
1094
1096
|
// ─── Task Sessions ─────────────────────────────────────
|
|
@@ -1170,7 +1172,9 @@ async function handleMessage(ws, msg, subscriptions) {
|
|
|
1170
1172
|
});
|
|
1171
1173
|
return;
|
|
1172
1174
|
}
|
|
1173
|
-
const environmentId =
|
|
1175
|
+
const environmentId = task.projectId
|
|
1176
|
+
? projectStore.getProject(task.projectId)?.defaultEnvironmentId
|
|
1177
|
+
: undefined;
|
|
1174
1178
|
if (!environmentId) {
|
|
1175
1179
|
sendWs(ws, {
|
|
1176
1180
|
type: "task_diff",
|