@grackle-ai/plugin-core 0.96.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codespace-handlers.d.ts +6 -0
- package/dist/codespace-handlers.d.ts.map +1 -0
- package/dist/codespace-handlers.js +66 -0
- package/dist/codespace-handlers.js.map +1 -0
- package/dist/cron-phase.d.ts +46 -0
- package/dist/cron-phase.d.ts.map +1 -0
- package/dist/cron-phase.js +88 -0
- package/dist/cron-phase.js.map +1 -0
- package/dist/dispatch-phase.d.ts +46 -0
- package/dist/dispatch-phase.d.ts.map +1 -0
- package/dist/dispatch-phase.js +99 -0
- package/dist/dispatch-phase.js.map +1 -0
- package/dist/environment-handlers.d.ts +16 -0
- package/dist/environment-handlers.d.ts.map +1 -0
- package/dist/environment-handlers.js +255 -0
- package/dist/environment-handlers.js.map +1 -0
- package/dist/environment-reconciliation.d.ts +35 -0
- package/dist/environment-reconciliation.d.ts.map +1 -0
- package/dist/environment-reconciliation.js +74 -0
- package/dist/environment-reconciliation.js.map +1 -0
- package/dist/escalation-handlers.d.ts +8 -0
- package/dist/escalation-handlers.d.ts.map +1 -0
- package/dist/escalation-handlers.js +60 -0
- package/dist/escalation-handlers.js.map +1 -0
- package/dist/finding-handlers.d.ts +8 -0
- package/dist/finding-handlers.d.ts.map +1 -0
- package/dist/finding-handlers.js +38 -0
- package/dist/finding-handlers.js.map +1 -0
- package/dist/grpc-proto-converters.d.ts +31 -0
- package/dist/grpc-proto-converters.d.ts.map +1 -0
- package/dist/grpc-proto-converters.js +222 -0
- package/dist/grpc-proto-converters.js.map +1 -0
- package/dist/grpc-service.d.ts +21 -0
- package/dist/grpc-service.d.ts.map +1 -0
- package/dist/grpc-service.js +50 -0
- package/dist/grpc-service.js.map +1 -0
- package/dist/grpc-shared.d.ts +22 -0
- package/dist/grpc-shared.d.ts.map +1 -0
- package/dist/grpc-shared.js +68 -0
- package/dist/grpc-shared.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge-handlers.d.ts +12 -0
- package/dist/knowledge-handlers.d.ts.map +1 -0
- package/dist/knowledge-handlers.js +149 -0
- package/dist/knowledge-handlers.js.map +1 -0
- package/dist/lifecycle-cleanup.d.ts +13 -0
- package/dist/lifecycle-cleanup.d.ts.map +1 -0
- package/dist/lifecycle-cleanup.js +35 -0
- package/dist/lifecycle-cleanup.js.map +1 -0
- package/dist/lifecycle.d.ts +28 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +132 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/orphan-phase.d.ts +27 -0
- package/dist/orphan-phase.d.ts.map +1 -0
- package/dist/orphan-phase.js +69 -0
- package/dist/orphan-phase.js.map +1 -0
- package/dist/persona-handlers.d.ts +12 -0
- package/dist/persona-handlers.d.ts.map +1 -0
- package/dist/persona-handlers.js +156 -0
- package/dist/persona-handlers.js.map +1 -0
- package/dist/root-task-boot.d.ts +67 -0
- package/dist/root-task-boot.d.ts.map +1 -0
- package/dist/root-task-boot.js +234 -0
- package/dist/root-task-boot.js.map +1 -0
- package/dist/schedule-expression.d.ts +43 -0
- package/dist/schedule-expression.d.ts.map +1 -0
- package/dist/schedule-expression.js +105 -0
- package/dist/schedule-expression.js.map +1 -0
- package/dist/schedule-handlers.d.ts +12 -0
- package/dist/schedule-handlers.d.ts.map +1 -0
- package/dist/schedule-handlers.js +122 -0
- package/dist/schedule-handlers.js.map +1 -0
- package/dist/session-handlers.d.ts +40 -0
- package/dist/session-handlers.d.ts.map +1 -0
- package/dist/session-handlers.js +610 -0
- package/dist/session-handlers.js.map +1 -0
- package/dist/settings-handlers.d.ts +10 -0
- package/dist/settings-handlers.d.ts.map +1 -0
- package/dist/settings-handlers.js +80 -0
- package/dist/settings-handlers.js.map +1 -0
- package/dist/signals/escalation-auto.d.ts +20 -0
- package/dist/signals/escalation-auto.d.ts.map +1 -0
- package/dist/signals/escalation-auto.js +126 -0
- package/dist/signals/escalation-auto.js.map +1 -0
- package/dist/signals/orphan-reparent.d.ts +31 -0
- package/dist/signals/orphan-reparent.d.ts.map +1 -0
- package/dist/signals/orphan-reparent.js +175 -0
- package/dist/signals/orphan-reparent.js.map +1 -0
- package/dist/signals/sigchld.d.ts +12 -0
- package/dist/signals/sigchld.d.ts.map +1 -0
- package/dist/signals/sigchld.js +177 -0
- package/dist/signals/sigchld.js.map +1 -0
- package/dist/task-handlers.d.ts +22 -0
- package/dist/task-handlers.d.ts.map +1 -0
- package/dist/task-handlers.js +517 -0
- package/dist/task-handlers.js.map +1 -0
- package/dist/test-utils/integration-setup.d.ts +11 -0
- package/dist/test-utils/integration-setup.d.ts.map +1 -0
- package/dist/test-utils/integration-setup.js +26 -0
- package/dist/test-utils/integration-setup.js.map +1 -0
- package/dist/test-utils/mock-database.d.ts +152 -0
- package/dist/test-utils/mock-database.d.ts.map +1 -0
- package/dist/test-utils/mock-database.js +169 -0
- package/dist/test-utils/mock-database.js.map +1 -0
- package/dist/token-handlers.d.ts +12 -0
- package/dist/token-handlers.d.ts.map +1 -0
- package/dist/token-handlers.js +85 -0
- package/dist/token-handlers.js.map +1 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/utils/format-gh-error.d.ts +6 -0
- package/dist/utils/format-gh-error.d.ts.map +1 -0
- package/dist/utils/format-gh-error.js +30 -0
- package/dist/utils/format-gh-error.js.map +1 -0
- package/dist/workspace-handlers.d.ts +16 -0
- package/dist/workspace-handlers.d.ts.map +1 -0
- package/dist/workspace-handlers.js +146 -0
- package/dist/workspace-handlers.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-handlers.js","sourceRoot":"","sources":["../src/knowledge-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle cleanup reconciliation phase — removes orphaned lifecycle streams
|
|
3
|
+
* whose sessions have been deleted from the database.
|
|
4
|
+
*
|
|
5
|
+
* Prevents unbounded memory growth from accumulating in-memory streams for
|
|
6
|
+
* sessions that no longer exist.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { ReconciliationPhase } from "@grackle-ai/core";
|
|
11
|
+
/** Reconciliation phase that cleans up lifecycle streams for deleted sessions. */
|
|
12
|
+
export declare const lifecycleCleanupPhase: ReconciliationPhase;
|
|
13
|
+
//# sourceMappingURL=lifecycle-cleanup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-cleanup.d.ts","sourceRoot":"","sources":["../src/lifecycle-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAO5D,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,EAAE,mBAkBnC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle cleanup reconciliation phase — removes orphaned lifecycle streams
|
|
3
|
+
* whose sessions have been deleted from the database.
|
|
4
|
+
*
|
|
5
|
+
* Prevents unbounded memory growth from accumulating in-memory streams for
|
|
6
|
+
* sessions that no longer exist.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { sessionStore } from "@grackle-ai/database";
|
|
11
|
+
import { streamRegistry } from "@grackle-ai/core";
|
|
12
|
+
import { logger } from "@grackle-ai/core";
|
|
13
|
+
/** Prefix for lifecycle stream names. */
|
|
14
|
+
const LIFECYCLE_PREFIX = "lifecycle:";
|
|
15
|
+
/** Reconciliation phase that cleans up lifecycle streams for deleted sessions. */
|
|
16
|
+
export const lifecycleCleanupPhase = {
|
|
17
|
+
name: "lifecycle-cleanup",
|
|
18
|
+
execute: async () => {
|
|
19
|
+
let cleaned = 0;
|
|
20
|
+
for (const stream of streamRegistry.listStreams()) {
|
|
21
|
+
if (!stream.name.startsWith(LIFECYCLE_PREFIX)) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const sessionId = stream.name.slice(LIFECYCLE_PREFIX.length);
|
|
25
|
+
if (!sessionStore.getSession(sessionId)) {
|
|
26
|
+
streamRegistry.deleteStream(stream.id);
|
|
27
|
+
cleaned++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (cleaned > 0) {
|
|
31
|
+
logger.info({ cleaned }, "Lifecycle cleanup: removed %d orphaned stream(s)", cleaned);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=lifecycle-cleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-cleanup.js","sourceRoot":"","sources":["../src/lifecycle-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,yCAAyC;AACzC,MAAM,gBAAgB,GAAW,YAAY,CAAC;AAE9C,kFAAkF;AAClF,MAAM,CAAC,MAAM,qBAAqB,GAAwB;IACxD,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,KAAK,IAAmB,EAAE;QACjC,IAAI,OAAO,GAAW,CAAC,CAAC;QACxB,KAAK,MAAM,MAAM,IAAI,cAAc,CAAC,WAAW,EAAE,EAAE,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAC9C,SAAS;YACX,CAAC;YACD,MAAM,SAAS,GAAW,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;YACrE,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBACxC,cAAc,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACvC,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,kDAAkD,EAAE,OAAO,CAAC,CAAC;QACxF,CAAC;IACH,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle manager — auto-stops sessions when all their file descriptors are closed.
|
|
3
|
+
*
|
|
4
|
+
* When the last subscription (fd) for a session is removed from the stream-registry,
|
|
5
|
+
* the orphan callback fires and this module:
|
|
6
|
+
* 1. Sets the session status to STOPPED with an appropriate end reason
|
|
7
|
+
* 2. Kills the PowerLine process (best-effort)
|
|
8
|
+
* 3. Broadcasts the end reason to UI clients
|
|
9
|
+
* 4. Emits a task.updated event if the session has a task
|
|
10
|
+
*
|
|
11
|
+
* This is the foundation of the emergent lifecycle model: session alive/dead is
|
|
12
|
+
* determined by subscription state, not explicit status calls.
|
|
13
|
+
*/
|
|
14
|
+
import { cleanupLifecycleStream, ensureLifecycleStream } from "@grackle-ai/core";
|
|
15
|
+
import type { Disposable, PluginContext } from "@grackle-ai/core";
|
|
16
|
+
export { cleanupLifecycleStream, ensureLifecycleStream };
|
|
17
|
+
/**
|
|
18
|
+
* Create the lifecycle manager subscriber.
|
|
19
|
+
*
|
|
20
|
+
* Registers orphan and revival callbacks on the stream-registry so that
|
|
21
|
+
* sessions auto-stop when all fds are closed and auto-reanimate when
|
|
22
|
+
* a new fd is opened.
|
|
23
|
+
*
|
|
24
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
25
|
+
* @returns A Disposable that unregisters both callbacks.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createLifecycleSubscriber(ctx: PluginContext): Disposable;
|
|
28
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,EAEL,sBAAsB,EAAE,qBAAqB,EAC9C,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIlE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;AAEzD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,aAAa,GAAG,UAAU,CAqHxE"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle manager — auto-stops sessions when all their file descriptors are closed.
|
|
3
|
+
*
|
|
4
|
+
* When the last subscription (fd) for a session is removed from the stream-registry,
|
|
5
|
+
* the orphan callback fires and this module:
|
|
6
|
+
* 1. Sets the session status to STOPPED with an appropriate end reason
|
|
7
|
+
* 2. Kills the PowerLine process (best-effort)
|
|
8
|
+
* 3. Broadcasts the end reason to UI clients
|
|
9
|
+
* 4. Emits a task.updated event if the session has a task
|
|
10
|
+
*
|
|
11
|
+
* This is the foundation of the emergent lifecycle model: session alive/dead is
|
|
12
|
+
* determined by subscription state, not explicit status calls.
|
|
13
|
+
*/
|
|
14
|
+
import { create } from "@bufbuild/protobuf";
|
|
15
|
+
import { grackle, SESSION_STATUS, TERMINAL_SESSION_STATUSES, END_REASON, powerline } from "@grackle-ai/common";
|
|
16
|
+
import { sessionStore, taskStore } from "@grackle-ai/database";
|
|
17
|
+
import { streamRegistry, adapterManager, streamHub, reanimateAgent, logger, cleanupLifecycleStream, ensureLifecycleStream, } from "@grackle-ai/core";
|
|
18
|
+
// Re-export lifecycle stream utilities so existing plugin-core consumers
|
|
19
|
+
// can continue to import from this module.
|
|
20
|
+
export { cleanupLifecycleStream, ensureLifecycleStream };
|
|
21
|
+
/**
|
|
22
|
+
* Create the lifecycle manager subscriber.
|
|
23
|
+
*
|
|
24
|
+
* Registers orphan and revival callbacks on the stream-registry so that
|
|
25
|
+
* sessions auto-stop when all fds are closed and auto-reanimate when
|
|
26
|
+
* a new fd is opened.
|
|
27
|
+
*
|
|
28
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
29
|
+
* @returns A Disposable that unregisters both callbacks.
|
|
30
|
+
*/
|
|
31
|
+
export function createLifecycleSubscriber(ctx) {
|
|
32
|
+
const unsubOrphan = streamRegistry.onSessionOrphaned((sessionId) => {
|
|
33
|
+
const session = sessionStore.getSession(sessionId);
|
|
34
|
+
if (!session) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const alreadyTerminal = TERMINAL_SESSION_STATUSES.has(session.status);
|
|
38
|
+
// Always kill the PowerLine process (best-effort — may have already exited).
|
|
39
|
+
// Even if the session is already STOPPED (e.g. killAgent pre-set it), the
|
|
40
|
+
// process may still be running and needs to be terminated.
|
|
41
|
+
const conn = adapterManager.getConnection(session.environmentId);
|
|
42
|
+
if (conn) {
|
|
43
|
+
// Use the session's endReason if already set (killAgent pre-set it),
|
|
44
|
+
// otherwise determine from the session's status at time of orphaning.
|
|
45
|
+
let reason;
|
|
46
|
+
if (alreadyTerminal && session.endReason) {
|
|
47
|
+
reason = session.endReason;
|
|
48
|
+
}
|
|
49
|
+
else if (!alreadyTerminal && session.status === SESSION_STATUS.IDLE) {
|
|
50
|
+
reason = session.sigtermSentAt ? END_REASON.TERMINATED : END_REASON.COMPLETED;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
reason = END_REASON.KILLED;
|
|
54
|
+
}
|
|
55
|
+
conn.client.kill(create(powerline.KillRequestSchema, { id: sessionId, reason })).catch((err) => {
|
|
56
|
+
logger.debug({ err, sessionId }, "Lifecycle: PowerLine kill failed (process may have already exited)");
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Skip status change and broadcast if already terminal (killAgent already handled it)
|
|
60
|
+
if (alreadyTerminal) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Determine reason: IDLE sessions completed naturally; others were killed.
|
|
64
|
+
// If SIGTERM was sent and the session reached IDLE before being orphaned,
|
|
65
|
+
// use TERMINATED instead of COMPLETED to distinguish graceful shutdowns.
|
|
66
|
+
const reason = session.status === SESSION_STATUS.IDLE
|
|
67
|
+
? (session.sigtermSentAt ? END_REASON.TERMINATED : END_REASON.COMPLETED)
|
|
68
|
+
: END_REASON.KILLED;
|
|
69
|
+
logger.info({ sessionId, previousStatus: session.status, reason }, "Session orphaned (no remaining fds) — stopping");
|
|
70
|
+
sessionStore.updateSession(sessionId, SESSION_STATUS.STOPPED, undefined, undefined, reason);
|
|
71
|
+
// Broadcast end reason to UI clients
|
|
72
|
+
streamHub.publish(create(grackle.SessionEventSchema, {
|
|
73
|
+
sessionId,
|
|
74
|
+
type: grackle.EventType.STATUS,
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
content: reason,
|
|
77
|
+
raw: "",
|
|
78
|
+
}));
|
|
79
|
+
// Notify task system if this session belongs to a task
|
|
80
|
+
if (session.taskId) {
|
|
81
|
+
const task = taskStore.getTask(session.taskId);
|
|
82
|
+
if (task) {
|
|
83
|
+
ctx.emit("task.updated", { taskId: task.id, workspaceId: task.workspaceId || "" });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// ── Auto-reanimate: when an external session subscribes to a lifecycle
|
|
88
|
+
// stream whose session is stopped, automatically restart it. This is the
|
|
89
|
+
// "open() IS reanimate" model from the streams IPC spec.
|
|
90
|
+
const unsubRevived = streamRegistry.onSessionRevived((targetSessionId, _subscriberSessionId) => {
|
|
91
|
+
const session = sessionStore.getSession(targetSessionId);
|
|
92
|
+
if (!session) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Only reanimate stopped or suspended sessions
|
|
96
|
+
if (!TERMINAL_SESSION_STATUSES.has(session.status) && session.status !== SESSION_STATUS.SUSPENDED) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Must have a runtimeSessionId to resume from JSONL
|
|
100
|
+
if (!session.runtimeSessionId) {
|
|
101
|
+
logger.debug({ targetSessionId }, "Auto-reanimate skipped: no runtimeSessionId");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Environment must not have another active session
|
|
105
|
+
const existingActive = sessionStore.getActiveForEnv(session.environmentId);
|
|
106
|
+
if (existingActive) {
|
|
107
|
+
logger.debug({ targetSessionId, existingActive: existingActive.id }, "Auto-reanimate skipped: environment busy");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Environment must be connected
|
|
111
|
+
const conn = adapterManager.getConnection(session.environmentId);
|
|
112
|
+
if (!conn) {
|
|
113
|
+
logger.debug({ targetSessionId }, "Auto-reanimate skipped: environment disconnected");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
logger.info({ targetSessionId }, "Auto-reanimating session on new lifecycle subscription");
|
|
117
|
+
try {
|
|
118
|
+
reanimateAgent(targetSessionId);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
logger.debug({ err, targetSessionId }, "Auto-reanimate failed (non-fatal)");
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
logger.info("Lifecycle manager initialized");
|
|
125
|
+
return {
|
|
126
|
+
dispose() {
|
|
127
|
+
unsubOrphan();
|
|
128
|
+
unsubRevived();
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orphan reconciliation phase — periodic safety net for orphaned tasks.
|
|
3
|
+
*
|
|
4
|
+
* Sweeps all tasks for children whose parent is in a terminal state but
|
|
5
|
+
* haven't been reparented by the event-driven handler. This catches edge
|
|
6
|
+
* cases like server restarts, race conditions, or missed events.
|
|
7
|
+
*/
|
|
8
|
+
import type { TaskRow } from "@grackle-ai/database";
|
|
9
|
+
import type { GrackleEventType } from "@grackle-ai/core";
|
|
10
|
+
import type { ReconciliationPhase } from "@grackle-ai/core";
|
|
11
|
+
/** Dependencies injected into the orphan phase for testability. */
|
|
12
|
+
export interface OrphanPhaseDeps {
|
|
13
|
+
/** Get all tasks (across all workspaces). */
|
|
14
|
+
listAllTasks: () => TaskRow[];
|
|
15
|
+
/** Reparent a task to a new parent. */
|
|
16
|
+
reparentTask: (taskId: string, newParentTaskId: string) => void;
|
|
17
|
+
/** Emit a domain event. */
|
|
18
|
+
emit: (type: GrackleEventType, payload: Record<string, unknown>) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create the orphan reconciliation phase.
|
|
22
|
+
*
|
|
23
|
+
* @param deps - Injected dependencies for testability.
|
|
24
|
+
* @returns A ReconciliationPhase that can be registered with the ReconciliationManager.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createOrphanPhase(deps: OrphanPhaseDeps): ReconciliationPhase;
|
|
27
|
+
//# sourceMappingURL=orphan-phase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orphan-phase.d.ts","sourceRoot":"","sources":["../src/orphan-phase.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAQ5D,mEAAmE;AACnE,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,YAAY,EAAE,MAAM,OAAO,EAAE,CAAC;IAC9B,uCAAuC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,2BAA2B;IAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAC1E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,mBAAmB,CA6D5E"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orphan reconciliation phase — periodic safety net for orphaned tasks.
|
|
3
|
+
*
|
|
4
|
+
* Sweeps all tasks for children whose parent is in a terminal state but
|
|
5
|
+
* haven't been reparented by the event-driven handler. This catches edge
|
|
6
|
+
* cases like server restarts, race conditions, or missed events.
|
|
7
|
+
*/
|
|
8
|
+
import { ROOT_TASK_ID, TASK_STATUS } from "@grackle-ai/common";
|
|
9
|
+
import { logger } from "@grackle-ai/core";
|
|
10
|
+
/** Terminal task statuses that indicate the parent is done. */
|
|
11
|
+
const TERMINAL_TASK_STATUSES = new Set([
|
|
12
|
+
TASK_STATUS.COMPLETE,
|
|
13
|
+
TASK_STATUS.FAILED,
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* Create the orphan reconciliation phase.
|
|
17
|
+
*
|
|
18
|
+
* @param deps - Injected dependencies for testability.
|
|
19
|
+
* @returns A ReconciliationPhase that can be registered with the ReconciliationManager.
|
|
20
|
+
*/
|
|
21
|
+
export function createOrphanPhase(deps) {
|
|
22
|
+
return {
|
|
23
|
+
name: "orphan-reparent",
|
|
24
|
+
execute: async () => {
|
|
25
|
+
const allTasks = deps.listAllTasks();
|
|
26
|
+
// Build a lookup map for quick parent resolution
|
|
27
|
+
const taskById = new Map(allTasks.map((t) => [t.id, t]));
|
|
28
|
+
let reparentCount = 0;
|
|
29
|
+
for (const task of allTasks) {
|
|
30
|
+
// Skip root tasks and tasks with no parent
|
|
31
|
+
if (!task.parentTaskId || task.parentTaskId === ROOT_TASK_ID) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// Skip terminal tasks (they don't need reparenting)
|
|
35
|
+
if (TERMINAL_TASK_STATUSES.has(task.status)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// Check if parent is terminal
|
|
39
|
+
const parent = taskById.get(task.parentTaskId);
|
|
40
|
+
if (!parent || !TERMINAL_TASK_STATUSES.has(parent.status)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// This is an orphan! Reparent to grandparent (or root)
|
|
44
|
+
const grandparentId = parent.parentTaskId || ROOT_TASK_ID;
|
|
45
|
+
try {
|
|
46
|
+
deps.reparentTask(task.id, grandparentId);
|
|
47
|
+
deps.emit("task.reparented", {
|
|
48
|
+
taskId: task.id,
|
|
49
|
+
oldParentTaskId: task.parentTaskId,
|
|
50
|
+
newParentTaskId: grandparentId,
|
|
51
|
+
workspaceId: task.workspaceId || "",
|
|
52
|
+
});
|
|
53
|
+
deps.emit("task.updated", {
|
|
54
|
+
taskId: task.id,
|
|
55
|
+
workspaceId: task.workspaceId || "",
|
|
56
|
+
});
|
|
57
|
+
reparentCount++;
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
logger.error({ err, taskId: task.id, parentTaskId: task.parentTaskId, grandparentId }, "Orphan phase: failed to reparent task");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (reparentCount > 0) {
|
|
64
|
+
logger.warn({ reparentCount }, "Orphan phase: reparented %d orphaned task(s) — these should have been caught by the event-driven handler", reparentCount);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=orphan-phase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orphan-phase.js","sourceRoot":"","sources":["../src/orphan-phase.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK1C,+DAA+D;AAC/D,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IAC1D,WAAW,CAAC,QAAQ;IACpB,WAAW,CAAC,MAAM;CACnB,CAAC,CAAC;AAYH;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAqB;IACrD,OAAO;QACL,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAErC,iDAAiD;YACjD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAkB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAE1E,IAAI,aAAa,GAAG,CAAC,CAAC;YAEtB,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,2CAA2C;gBAC3C,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,KAAK,YAAY,EAAE,CAAC;oBAC7D,SAAS;gBACX,CAAC;gBAED,oDAAoD;gBACpD,IAAI,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC5C,SAAS;gBACX,CAAC;gBAED,8BAA8B;gBAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAC/C,IAAI,CAAC,MAAM,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1D,SAAS;gBACX,CAAC;gBAED,uDAAuD;gBACvD,MAAM,aAAa,GAAG,MAAM,CAAC,YAAY,IAAI,YAAY,CAAC;gBAE1D,IAAI,CAAC;oBACH,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;oBAC1C,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE;wBAC3B,MAAM,EAAE,IAAI,CAAC,EAAE;wBACf,eAAe,EAAE,IAAI,CAAC,YAAY;wBAClC,eAAe,EAAE,aAAa;wBAC9B,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;qBACpC,CAAC,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;wBACxB,MAAM,EAAE,IAAI,CAAC,EAAE;wBACf,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;qBACpC,CAAC,CAAC;oBACH,aAAa,EAAE,CAAC;gBAClB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,KAAK,CACV,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,EAAE,EACxE,uCAAuC,CACxC,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CACT,EAAE,aAAa,EAAE,EACjB,0GAA0G,EAC1G,aAAa,CACd,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { grackle } from "@grackle-ai/common";
|
|
2
|
+
/** List all personas. */
|
|
3
|
+
export declare function listPersonas(): Promise<grackle.PersonaList>;
|
|
4
|
+
/** Create a new persona. */
|
|
5
|
+
export declare function createPersona(req: grackle.CreatePersonaRequest): Promise<grackle.Persona>;
|
|
6
|
+
/** Get a persona by ID. */
|
|
7
|
+
export declare function getPersona(req: grackle.PersonaId): Promise<grackle.Persona>;
|
|
8
|
+
/** Update an existing persona. */
|
|
9
|
+
export declare function updatePersona(req: grackle.UpdatePersonaRequest): Promise<grackle.Persona>;
|
|
10
|
+
/** Delete a persona by ID. */
|
|
11
|
+
export declare function deletePersona(req: grackle.PersonaId): Promise<grackle.Empty>;
|
|
12
|
+
//# sourceMappingURL=persona-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persona-handlers.d.ts","sourceRoot":"","sources":["../src/persona-handlers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAQ7C,yBAAyB;AACzB,wBAAsB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAKjE;AAED,4BAA4B;AAC5B,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAsE/F;AAED,2BAA2B;AAC3B,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAMjF;AAED,kCAAkC;AAClC,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAkG/F;AAED,8BAA8B;AAC9B,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAIlF"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { ConnectError, Code } from "@connectrpc/connect";
|
|
2
|
+
import { create } from "@bufbuild/protobuf";
|
|
3
|
+
import { grackle } from "@grackle-ai/common";
|
|
4
|
+
import { ALL_MCP_TOOL_NAMES } from "@grackle-ai/common";
|
|
5
|
+
import { personaStore, settingsStore, envRegistry } from "@grackle-ai/database";
|
|
6
|
+
import { v4 as uuid } from "uuid";
|
|
7
|
+
import { slugify } from "@grackle-ai/database";
|
|
8
|
+
import { emit } from "@grackle-ai/core";
|
|
9
|
+
import { personaRowToProto } from "./grpc-proto-converters.js";
|
|
10
|
+
/** List all personas. */
|
|
11
|
+
export async function listPersonas() {
|
|
12
|
+
const rows = personaStore.listPersonas();
|
|
13
|
+
return create(grackle.PersonaListSchema, {
|
|
14
|
+
personas: rows.map(personaRowToProto),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/** Create a new persona. */
|
|
18
|
+
export async function createPersona(req) {
|
|
19
|
+
if (!req.name) {
|
|
20
|
+
throw new ConnectError("Persona name is required", Code.InvalidArgument);
|
|
21
|
+
}
|
|
22
|
+
const personaType = req.type || "agent";
|
|
23
|
+
if (personaType !== "agent" && personaType !== "script") {
|
|
24
|
+
throw new ConnectError(`Invalid persona type: "${personaType}". Must be "agent" or "script".`, Code.InvalidArgument);
|
|
25
|
+
}
|
|
26
|
+
if (personaType === "script") {
|
|
27
|
+
if (!req.script) {
|
|
28
|
+
throw new ConnectError("Script content is required for script personas", Code.InvalidArgument);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
if (!req.systemPrompt) {
|
|
33
|
+
throw new ConnectError("Persona system_prompt is required", Code.InvalidArgument);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Enforce unique ID and unique name
|
|
37
|
+
let id = slugify(req.name) || uuid().slice(0, 8);
|
|
38
|
+
if (personaStore.getPersona(id)) {
|
|
39
|
+
id = `${id}-${uuid().slice(0, 4)}`;
|
|
40
|
+
}
|
|
41
|
+
if (personaStore.getPersonaByName(req.name)) {
|
|
42
|
+
throw new ConnectError(`Persona with name "${req.name}" already exists`, Code.AlreadyExists);
|
|
43
|
+
}
|
|
44
|
+
const toolConfigJson = JSON.stringify({
|
|
45
|
+
allowedTools: [...(req.toolConfig?.allowedTools || [])],
|
|
46
|
+
disallowedTools: [...(req.toolConfig?.disallowedTools || [])],
|
|
47
|
+
});
|
|
48
|
+
const mcpServersJson = JSON.stringify(req.mcpServers.map((s) => ({
|
|
49
|
+
name: s.name,
|
|
50
|
+
command: s.command,
|
|
51
|
+
args: [...s.args],
|
|
52
|
+
tools: [...s.tools],
|
|
53
|
+
})));
|
|
54
|
+
// Validate allowed MCP tools against the known tool registry
|
|
55
|
+
const allowedMcpTools = Array.isArray(req.allowedMcpTools) ? [...req.allowedMcpTools] : [];
|
|
56
|
+
if (allowedMcpTools.length > 0) {
|
|
57
|
+
const invalid = allowedMcpTools.filter((t) => !ALL_MCP_TOOL_NAMES.has(t));
|
|
58
|
+
if (invalid.length > 0) {
|
|
59
|
+
throw new ConnectError(`Invalid MCP tool name(s): ${invalid.join(", ")}`, Code.InvalidArgument);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const allowedMcpToolsJson = JSON.stringify(allowedMcpTools);
|
|
63
|
+
personaStore.createPersona(id, req.name, req.description, req.systemPrompt, toolConfigJson, req.runtime, req.model, req.maxTurns, mcpServersJson, personaType, req.script, allowedMcpToolsJson);
|
|
64
|
+
emit("persona.created", { personaId: id });
|
|
65
|
+
const row = personaStore.getPersona(id);
|
|
66
|
+
return personaRowToProto(row);
|
|
67
|
+
}
|
|
68
|
+
/** Get a persona by ID. */
|
|
69
|
+
export async function getPersona(req) {
|
|
70
|
+
const row = personaStore.getPersona(req.id);
|
|
71
|
+
if (!row) {
|
|
72
|
+
throw new ConnectError(`Persona not found: ${req.id}`, Code.NotFound);
|
|
73
|
+
}
|
|
74
|
+
return personaRowToProto(row);
|
|
75
|
+
}
|
|
76
|
+
/** Update an existing persona. */
|
|
77
|
+
export async function updatePersona(req) {
|
|
78
|
+
const existing = personaStore.getPersona(req.id);
|
|
79
|
+
if (!existing) {
|
|
80
|
+
throw new ConnectError(`Persona not found: ${req.id}`, Code.NotFound);
|
|
81
|
+
}
|
|
82
|
+
// Only update toolConfig/mcpServers if the request provides non-empty values;
|
|
83
|
+
// otherwise keep the existing stored value.
|
|
84
|
+
const hasNewToolConfig = !!req.toolConfig &&
|
|
85
|
+
(req.toolConfig.allowedTools.length > 0 ||
|
|
86
|
+
req.toolConfig.disallowedTools.length > 0);
|
|
87
|
+
const toolConfigJson = hasNewToolConfig
|
|
88
|
+
? JSON.stringify({
|
|
89
|
+
allowedTools: [...(req.toolConfig?.allowedTools || [])],
|
|
90
|
+
disallowedTools: [...(req.toolConfig?.disallowedTools || [])],
|
|
91
|
+
})
|
|
92
|
+
: existing.toolConfig;
|
|
93
|
+
const hasNewMcpServers = Array.isArray(req.mcpServers) && req.mcpServers.length > 0;
|
|
94
|
+
const mcpServersJson = hasNewMcpServers
|
|
95
|
+
? JSON.stringify(req.mcpServers.map((s) => ({
|
|
96
|
+
name: s.name,
|
|
97
|
+
command: s.command,
|
|
98
|
+
args: [...s.args],
|
|
99
|
+
tools: [...s.tools],
|
|
100
|
+
})))
|
|
101
|
+
: existing.mcpServers;
|
|
102
|
+
// Treat empty string / 0 as "not set" and keep existing value
|
|
103
|
+
const name = req.name || existing.name;
|
|
104
|
+
if (name !== existing.name && personaStore.getPersonaByName(name)) {
|
|
105
|
+
throw new ConnectError(`Persona with name "${name}" already exists`, Code.AlreadyExists);
|
|
106
|
+
}
|
|
107
|
+
const description = req.description || existing.description;
|
|
108
|
+
const systemPrompt = req.systemPrompt || existing.systemPrompt;
|
|
109
|
+
const runtime = req.runtime || existing.runtime;
|
|
110
|
+
const model = req.model || existing.model;
|
|
111
|
+
const maxTurns = req.maxTurns === 0 ? existing.maxTurns : req.maxTurns;
|
|
112
|
+
// Empty string means "keep existing", non-empty means "set to this value"
|
|
113
|
+
const updatedType = req.type || existing.type;
|
|
114
|
+
const updatedScript = req.script || existing.script;
|
|
115
|
+
// AllowedMcpTools is a wrapper message with proto3 presence tracking:
|
|
116
|
+
// - absent (undefined) → preserve existing value
|
|
117
|
+
// - present with empty tools → clear to default (revert to full set)
|
|
118
|
+
// - present with tools → validate and replace
|
|
119
|
+
let allowedMcpToolsJson;
|
|
120
|
+
if (req.allowedMcpTools) {
|
|
121
|
+
const tools = [...req.allowedMcpTools.tools];
|
|
122
|
+
if (tools.length > 0) {
|
|
123
|
+
const invalid = tools.filter((t) => !ALL_MCP_TOOL_NAMES.has(t));
|
|
124
|
+
if (invalid.length > 0) {
|
|
125
|
+
throw new ConnectError(`Invalid MCP tool name(s): ${invalid.join(", ")}`, Code.InvalidArgument);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
allowedMcpToolsJson = JSON.stringify(tools);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
allowedMcpToolsJson = existing.allowedMcpTools;
|
|
132
|
+
}
|
|
133
|
+
personaStore.updatePersona(req.id, name, description, systemPrompt, toolConfigJson, runtime, model, maxTurns, mcpServersJson, updatedType, updatedScript, allowedMcpToolsJson);
|
|
134
|
+
// Sync the local environment's defaultRuntime when the app-level default
|
|
135
|
+
// persona's runtime changes, so bootstrap pre-installs the correct packages.
|
|
136
|
+
if (runtime !== existing.runtime) {
|
|
137
|
+
const appDefault = settingsStore.getSetting("default_persona_id") || "";
|
|
138
|
+
if (appDefault === req.id) {
|
|
139
|
+
const localEnv = envRegistry.getEnvironment("local");
|
|
140
|
+
if (localEnv) {
|
|
141
|
+
envRegistry.updateDefaultRuntime("local", runtime);
|
|
142
|
+
emit("environment.changed", {});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
emit("persona.updated", { personaId: req.id });
|
|
147
|
+
const row = personaStore.getPersona(req.id);
|
|
148
|
+
return personaRowToProto(row);
|
|
149
|
+
}
|
|
150
|
+
/** Delete a persona by ID. */
|
|
151
|
+
export async function deletePersona(req) {
|
|
152
|
+
personaStore.deletePersona(req.id);
|
|
153
|
+
emit("persona.deleted", { personaId: req.id });
|
|
154
|
+
return create(grackle.EmptySchema, {});
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=persona-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persona-handlers.js","sourceRoot":"","sources":["../src/persona-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,yBAAyB;AACzB,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,IAAI,GAAG,YAAY,CAAC,YAAY,EAAE,CAAC;IACzC,OAAO,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE;QACvC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC;KACtC,CAAC,CAAC;AACL,CAAC;AAED,4BAA4B;AAC5B,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAiC;IACnE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,IAAI,YAAY,CAAC,0BAA0B,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC;IACxC,IAAI,WAAW,KAAK,OAAO,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;QACxD,MAAM,IAAI,YAAY,CAAC,0BAA0B,WAAW,iCAAiC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACvH,CAAC;IACD,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,YAAY,CAAC,gDAAgD,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,IAAI,YAAY,CAAC,mCAAmC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACjD,IAAI,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;QAChC,EAAE,GAAG,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IACrC,CAAC;IACD,IAAI,YAAY,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,IAAI,kBAAkB,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC/F,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC;QACpC,YAAY,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,YAAY,IAAI,EAAE,CAAC,CAAC;QACvD,eAAe,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,eAAe,IAAI,EAAE,CAAC,CAAC;KAC9D,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CACnC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACjB,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;KACpB,CAAC,CAAC,CACJ,CAAC;IAEF,6DAA6D;IAC7D,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3F,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,YAAY,CACpB,6BAA6B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACjD,IAAI,CAAC,eAAe,CACrB,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAE5D,YAAY,CAAC,aAAa,CACxB,EAAE,EACF,GAAG,CAAC,IAAI,EACR,GAAG,CAAC,WAAW,EACf,GAAG,CAAC,YAAY,EAChB,cAAc,EACd,GAAG,CAAC,OAAO,EACX,GAAG,CAAC,KAAK,EACT,GAAG,CAAC,QAAQ,EACZ,cAAc,EACd,WAAW,EACX,GAAG,CAAC,MAAM,EACV,mBAAmB,CACpB,CAAC;IACF,IAAI,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACxC,OAAO,iBAAiB,CAAC,GAAI,CAAC,CAAC;AACjC,CAAC;AAED,2BAA2B;AAC3B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAsB;IACrD,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,kCAAkC;AAClC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAiC;IACnE,MAAM,QAAQ,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC;IAED,8EAA8E;IAC9E,4CAA4C;IAC5C,MAAM,gBAAgB,GACpB,CAAC,CAAC,GAAG,CAAC,UAAU;QAChB,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;YACrC,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/C,MAAM,cAAc,GAAG,gBAAgB;QACrC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;YACb,YAAY,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,YAAY,IAAI,EAAE,CAAC,CAAC;YACvD,eAAe,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,eAAe,IAAI,EAAE,CAAC,CAAC;SAC9D,CAAC;QACJ,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;IAExB,MAAM,gBAAgB,GACpB,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7D,MAAM,cAAc,GAAG,gBAAgB;QACrC,CAAC,CAAC,IAAI,CAAC,SAAS,CACZ,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACjB,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;SACpB,CAAC,CAAC,CACJ;QACH,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;IAExB,8DAA8D;IAC9D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC;IACvC,IAAI,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,YAAY,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,YAAY,CAAC,sBAAsB,IAAI,kBAAkB,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC;IAC5D,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,IAAI,QAAQ,CAAC,YAAY,CAAC;IAC/D,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC;IAChD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;IAC1C,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;IACvE,0EAA0E;IAC1E,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC;IAC9C,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC;IAEpD,sEAAsE;IACtE,iDAAiD;IACjD,qEAAqE;IACrE,8CAA8C;IAC9C,IAAI,mBAA2B,CAAC;IAChC,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,YAAY,CACpB,6BAA6B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACjD,IAAI,CAAC,eAAe,CACrB,CAAC;YACJ,CAAC;QACH,CAAC;QACD,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;SAAM,CAAC;QACN,mBAAmB,GAAG,QAAQ,CAAC,eAAe,CAAC;IACjD,CAAC;IAED,YAAY,CAAC,aAAa,CACxB,GAAG,CAAC,EAAE,EACN,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,cAAc,EACd,OAAO,EACP,KAAK,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,aAAa,EACb,mBAAmB,CACpB,CAAC;IAEF,yEAAyE;IACzE,6EAA6E;IAC7E,IAAI,OAAO,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;QACxE,IAAI,UAAU,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YACrD,IAAI,QAAQ,EAAE,CAAC;gBACb,WAAW,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACnD,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5C,OAAO,iBAAiB,CAAC,GAAI,CAAC,CAAC;AACjC,CAAC;AAED,8BAA8B;AAC9B,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAsB;IACxD,YAAY,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root task boot with reanimate-first strategy and exponential backoff.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the inline closure in `server/src/index.ts` so it can be
|
|
5
|
+
* unit tested. Follows the dependency-injection pattern established by
|
|
6
|
+
* `cron-phase.ts`.
|
|
7
|
+
*
|
|
8
|
+
* On each invocation (triggered by `environment.changed` events):
|
|
9
|
+
* 1. Checks if the root task is already running — if so, tracks stability.
|
|
10
|
+
* 2. If not running, attempts to reanimate the most recent session (preserving
|
|
11
|
+
* conversation history) before falling back to a fresh spawn.
|
|
12
|
+
* 3. Applies exponential backoff after consecutive failures to prevent
|
|
13
|
+
* crash-loop resource waste.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import type { TaskRow, SessionRow, EnvironmentRow } from "@grackle-ai/database";
|
|
18
|
+
import type { TaskStatusResult } from "@grackle-ai/core";
|
|
19
|
+
import type { Disposable, PluginContext } from "@grackle-ai/core";
|
|
20
|
+
/** Dependencies injected into the root task boot module for testability. */
|
|
21
|
+
export interface RootTaskBootDeps {
|
|
22
|
+
/** Look up a task by ID. */
|
|
23
|
+
getTask: (id: string) => TaskRow | undefined;
|
|
24
|
+
/** List all sessions for a task. */
|
|
25
|
+
listSessionsForTask: (taskId: string) => Pick<SessionRow, "id" | "status" | "startedAt">[];
|
|
26
|
+
/** Get the most recent session for a task (by startedAt DESC). */
|
|
27
|
+
getLatestSessionForTask: (taskId: string) => SessionRow | undefined;
|
|
28
|
+
/** Compute effective task status from stored status + session history. */
|
|
29
|
+
computeTaskStatus: (storedStatus: string, sessions: Pick<SessionRow, "id" | "status" | "startedAt">[]) => TaskStatusResult;
|
|
30
|
+
/** Find the first connected environment, preferring local. */
|
|
31
|
+
findFirstConnectedEnvironment: () => EnvironmentRow | undefined;
|
|
32
|
+
/** Start a new agent session for a task. Returns error string on failure, undefined on success. */
|
|
33
|
+
startTaskSession: (task: TaskRow, options?: {
|
|
34
|
+
environmentId?: string;
|
|
35
|
+
notes?: string;
|
|
36
|
+
}) => Promise<string | undefined>;
|
|
37
|
+
/** Reanimate a terminal session by resuming it on PowerLine. Throws on failure. */
|
|
38
|
+
reanimateAgent: (sessionId: string) => SessionRow;
|
|
39
|
+
/** Whether onboarding is complete. Boot is deferred until the user has chosen a runtime (#1031). */
|
|
40
|
+
isOnboarded?: () => boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create the root task boot handler.
|
|
44
|
+
*
|
|
45
|
+
* Returns a callable async function that can be wired to `environment.changed`
|
|
46
|
+
* event subscriptions. Each call checks whether the root task needs starting
|
|
47
|
+
* and applies reanimate-first + exponential backoff logic.
|
|
48
|
+
*
|
|
49
|
+
* Each call creates independent backoff state (not shared across calls).
|
|
50
|
+
*
|
|
51
|
+
* @param deps - Injected dependencies for testability.
|
|
52
|
+
* @returns An async function to call on each `environment.changed` event.
|
|
53
|
+
*/
|
|
54
|
+
export declare function createRootTaskBoot(deps: RootTaskBootDeps): () => Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Create the root task boot subscriber.
|
|
57
|
+
*
|
|
58
|
+
* Creates independent backoff state (not shared with other factory calls)
|
|
59
|
+
* and subscribes to `environment.changed` and `setting.changed` events.
|
|
60
|
+
* Returns a Disposable that unsubscribes the handler.
|
|
61
|
+
*
|
|
62
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
63
|
+
* @param deps - Injected dependencies for testability.
|
|
64
|
+
* @returns A Disposable that unsubscribes the handler.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createRootTaskBootSubscriber(ctx: PluginContext, deps: RootTaskBootDeps): Disposable;
|
|
67
|
+
//# sourceMappingURL=root-task-boot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-task-boot.d.ts","sourceRoot":"","sources":["../src/root-task-boot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAqBlE,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,4BAA4B;IAC5B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAC7C,oCAAoC;IACpC,mBAAmB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,QAAQ,GAAG,WAAW,CAAC,EAAE,CAAC;IAC3F,kEAAkE;IAClE,uBAAuB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;IACpE,0EAA0E;IAC1E,iBAAiB,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,QAAQ,GAAG,WAAW,CAAC,EAAE,KAAK,gBAAgB,CAAC;IAC3H,8DAA8D;IAC9D,6BAA6B,EAAE,MAAM,cAAc,GAAG,SAAS,CAAC;IAChE,mGAAmG;IACnG,gBAAgB,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvH,mFAAmF;IACnF,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,UAAU,CAAC;IAClD,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;CAC7B;AA4BD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAE9E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,GAAG,UAAU,CAuBnG"}
|