@grackle-ai/core 0.75.4

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.
Files changed (130) hide show
  1. package/README.md +30 -0
  2. package/dist/adapter-config.d.ts +6 -0
  3. package/dist/adapter-config.d.ts.map +1 -0
  4. package/dist/adapter-config.js +19 -0
  5. package/dist/adapter-config.js.map +1 -0
  6. package/dist/adapter-manager.d.ts +22 -0
  7. package/dist/adapter-manager.d.ts.map +1 -0
  8. package/dist/adapter-manager.js +81 -0
  9. package/dist/adapter-manager.js.map +1 -0
  10. package/dist/auto-reconnect.d.ts +23 -0
  11. package/dist/auto-reconnect.d.ts.map +1 -0
  12. package/dist/auto-reconnect.js +164 -0
  13. package/dist/auto-reconnect.js.map +1 -0
  14. package/dist/compute-task-status.d.ts +28 -0
  15. package/dist/compute-task-status.d.ts.map +1 -0
  16. package/dist/compute-task-status.js +70 -0
  17. package/dist/compute-task-status.js.map +1 -0
  18. package/dist/credential-bundle.d.ts +12 -0
  19. package/dist/credential-bundle.d.ts.map +1 -0
  20. package/dist/credential-bundle.js +183 -0
  21. package/dist/credential-bundle.js.map +1 -0
  22. package/dist/event-bus.d.ts +37 -0
  23. package/dist/event-bus.d.ts.map +1 -0
  24. package/dist/event-bus.js +65 -0
  25. package/dist/event-bus.js.map +1 -0
  26. package/dist/event-processor.d.ts +36 -0
  27. package/dist/event-processor.d.ts.map +1 -0
  28. package/dist/event-processor.js +312 -0
  29. package/dist/event-processor.js.map +1 -0
  30. package/dist/grpc-service.d.ts +22 -0
  31. package/dist/grpc-service.d.ts.map +1 -0
  32. package/dist/grpc-service.js +1724 -0
  33. package/dist/grpc-service.js.map +1 -0
  34. package/dist/index.d.ts +16 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +25 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/knowledge-init.d.ts +27 -0
  39. package/dist/knowledge-init.d.ts.map +1 -0
  40. package/dist/knowledge-init.js +212 -0
  41. package/dist/knowledge-init.js.map +1 -0
  42. package/dist/lifecycle.d.ts +36 -0
  43. package/dist/lifecycle.d.ts.map +1 -0
  44. package/dist/lifecycle.js +112 -0
  45. package/dist/lifecycle.js.map +1 -0
  46. package/dist/log-writer.d.ts +32 -0
  47. package/dist/log-writer.d.ts.map +1 -0
  48. package/dist/log-writer.js +104 -0
  49. package/dist/log-writer.js.map +1 -0
  50. package/dist/logger.d.ts +4 -0
  51. package/dist/logger.d.ts.map +1 -0
  52. package/dist/logger.js +10 -0
  53. package/dist/logger.js.map +1 -0
  54. package/dist/pipe-delivery.d.ts +41 -0
  55. package/dist/pipe-delivery.d.ts.map +1 -0
  56. package/dist/pipe-delivery.js +186 -0
  57. package/dist/pipe-delivery.js.map +1 -0
  58. package/dist/processor-registry.d.ts +25 -0
  59. package/dist/processor-registry.d.ts.map +1 -0
  60. package/dist/processor-registry.js +58 -0
  61. package/dist/processor-registry.js.map +1 -0
  62. package/dist/reanimate-agent.d.ts +12 -0
  63. package/dist/reanimate-agent.d.ts.map +1 -0
  64. package/dist/reanimate-agent.js +76 -0
  65. package/dist/reanimate-agent.js.map +1 -0
  66. package/dist/session-recovery.d.ts +16 -0
  67. package/dist/session-recovery.d.ts.map +1 -0
  68. package/dist/session-recovery.js +129 -0
  69. package/dist/session-recovery.js.map +1 -0
  70. package/dist/signals/sigchld.d.ts +7 -0
  71. package/dist/signals/sigchld.d.ts.map +1 -0
  72. package/dist/signals/sigchld.js +167 -0
  73. package/dist/signals/sigchld.js.map +1 -0
  74. package/dist/signals/signal-delivery.d.ts +14 -0
  75. package/dist/signals/signal-delivery.d.ts.map +1 -0
  76. package/dist/signals/signal-delivery.js +166 -0
  77. package/dist/signals/signal-delivery.js.map +1 -0
  78. package/dist/stream-hub.d.ts +14 -0
  79. package/dist/stream-hub.d.ts.map +1 -0
  80. package/dist/stream-hub.js +95 -0
  81. package/dist/stream-hub.js.map +1 -0
  82. package/dist/stream-registry.d.ts +84 -0
  83. package/dist/stream-registry.d.ts.map +1 -0
  84. package/dist/stream-registry.js +363 -0
  85. package/dist/stream-registry.js.map +1 -0
  86. package/dist/test-utils/integration-setup.d.ts +11 -0
  87. package/dist/test-utils/integration-setup.d.ts.map +1 -0
  88. package/dist/test-utils/integration-setup.js +32 -0
  89. package/dist/test-utils/integration-setup.js.map +1 -0
  90. package/dist/test-utils/mock-database.d.ts +130 -0
  91. package/dist/test-utils/mock-database.d.ts.map +1 -0
  92. package/dist/test-utils/mock-database.js +147 -0
  93. package/dist/test-utils/mock-database.js.map +1 -0
  94. package/dist/token-push.d.ts +22 -0
  95. package/dist/token-push.d.ts.map +1 -0
  96. package/dist/token-push.js +78 -0
  97. package/dist/token-push.js.map +1 -0
  98. package/dist/transcript.d.ts +5 -0
  99. package/dist/transcript.d.ts.map +1 -0
  100. package/dist/transcript.js +71 -0
  101. package/dist/transcript.js.map +1 -0
  102. package/dist/utils/exec.d.ts +17 -0
  103. package/dist/utils/exec.d.ts.map +1 -0
  104. package/dist/utils/exec.js +21 -0
  105. package/dist/utils/exec.js.map +1 -0
  106. package/dist/utils/format-gh-error.d.ts +6 -0
  107. package/dist/utils/format-gh-error.d.ts.map +1 -0
  108. package/dist/utils/format-gh-error.js +30 -0
  109. package/dist/utils/format-gh-error.js.map +1 -0
  110. package/dist/utils/network.d.ts +7 -0
  111. package/dist/utils/network.d.ts.map +1 -0
  112. package/dist/utils/network.js +21 -0
  113. package/dist/utils/network.js.map +1 -0
  114. package/dist/utils/ports.d.ts +3 -0
  115. package/dist/utils/ports.d.ts.map +1 -0
  116. package/dist/utils/ports.js +19 -0
  117. package/dist/utils/ports.js.map +1 -0
  118. package/dist/utils/sleep.d.ts +3 -0
  119. package/dist/utils/sleep.d.ts.map +1 -0
  120. package/dist/utils/sleep.js +5 -0
  121. package/dist/utils/sleep.js.map +1 -0
  122. package/dist/ws-bridge.d.ts +30 -0
  123. package/dist/ws-bridge.d.ts.map +1 -0
  124. package/dist/ws-bridge.js +372 -0
  125. package/dist/ws-bridge.js.map +1 -0
  126. package/dist/ws-broadcast.d.ts +19 -0
  127. package/dist/ws-broadcast.d.ts.map +1 -0
  128. package/dist/ws-broadcast.js +60 -0
  129. package/dist/ws-broadcast.js.map +1 -0
  130. package/package.json +57 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { type grackle } from "@grackle-ai/common";
2
+ /** Initialize a JSONL log stream for a session at the given directory path. */
3
+ export declare function initLog(logPath: string): void;
4
+ /**
5
+ * Ensure the JSONL log stream for the given path is open.
6
+ * If `initLog` has already been called for this path this is a no-op;
7
+ * otherwise it opens a new append stream. Use this before `writeEvent`
8
+ * when the caller cannot guarantee that `initLog` has already been called
9
+ * (e.g. signal delivery to a PENDING session).
10
+ */
11
+ export declare function ensureLogInitialized(logPath: string): void;
12
+ /** Append a session event as a JSON line to the session's log file. */
13
+ export declare function writeEvent(logPath: string, event: grackle.SessionEvent): void;
14
+ /** Close the write stream for a session log. */
15
+ export declare function endSession(logPath: string): void;
16
+ /** Deserialized shape of a single line in a session's `stream.jsonl` log. */
17
+ export interface LogEntry {
18
+ session_id: string;
19
+ type: string;
20
+ timestamp: string;
21
+ content: string;
22
+ raw?: string;
23
+ }
24
+ /**
25
+ * Read the last "text" entry from a session's JSONL log file without parsing the whole file.
26
+ * Reads only the tail of the file (up to LOG_TAIL_BYTES) to limit the amount of synchronous
27
+ * work and reduce event-loop blocking time for large sessions.
28
+ */
29
+ export declare function readLastTextEntry(logPath: string): LogEntry | undefined;
30
+ /** Read and parse all log entries from a session's JSONL log file. */
31
+ export declare function readLog(logPath: string): LogEntry[];
32
+ //# sourceMappingURL=log-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-writer.d.ts","sourceRoot":"","sources":["../src/log-writer.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,OAAO,EAAqB,MAAM,oBAAoB,CAAC;AAIrE,+EAA+E;AAC/E,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAK7C;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,GAAG,IAAI,CAa7E;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMhD;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAKD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAyCvE;AAED,sEAAsE;AACtE,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CASnD"}
@@ -0,0 +1,104 @@
1
+ import { createWriteStream, mkdirSync, readFileSync, existsSync, statSync, openSync, readSync, closeSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { eventTypeToString } from "@grackle-ai/common";
4
+ const openStreams = new Map();
5
+ /** Initialize a JSONL log stream for a session at the given directory path. */
6
+ export function initLog(logPath) {
7
+ mkdirSync(logPath, { recursive: true });
8
+ const streamPath = join(logPath, "stream.jsonl");
9
+ const ws = createWriteStream(streamPath, { flags: "a" });
10
+ openStreams.set(logPath, ws);
11
+ }
12
+ /**
13
+ * Ensure the JSONL log stream for the given path is open.
14
+ * If `initLog` has already been called for this path this is a no-op;
15
+ * otherwise it opens a new append stream. Use this before `writeEvent`
16
+ * when the caller cannot guarantee that `initLog` has already been called
17
+ * (e.g. signal delivery to a PENDING session).
18
+ */
19
+ export function ensureLogInitialized(logPath) {
20
+ if (!openStreams.has(logPath)) {
21
+ initLog(logPath);
22
+ }
23
+ }
24
+ /** Append a session event as a JSON line to the session's log file. */
25
+ export function writeEvent(logPath, event) {
26
+ const ws = openStreams.get(logPath);
27
+ if (!ws)
28
+ return;
29
+ const line = JSON.stringify({
30
+ session_id: event.sessionId,
31
+ type: eventTypeToString(event.type),
32
+ timestamp: event.timestamp,
33
+ content: event.content,
34
+ raw: event.raw || undefined,
35
+ });
36
+ ws.write(line + "\n");
37
+ }
38
+ /** Close the write stream for a session log. */
39
+ export function endSession(logPath) {
40
+ const ws = openStreams.get(logPath);
41
+ if (ws) {
42
+ ws.end();
43
+ openStreams.delete(logPath);
44
+ }
45
+ }
46
+ /** Number of bytes to read from the tail of a log file when searching for the last text entry. */
47
+ const LOG_TAIL_BYTES = 65536; // 64 KB
48
+ /**
49
+ * Read the last "text" entry from a session's JSONL log file without parsing the whole file.
50
+ * Reads only the tail of the file (up to LOG_TAIL_BYTES) to limit the amount of synchronous
51
+ * work and reduce event-loop blocking time for large sessions.
52
+ */
53
+ export function readLastTextEntry(logPath) {
54
+ const streamPath = join(logPath, "stream.jsonl");
55
+ if (!existsSync(streamPath)) {
56
+ return undefined;
57
+ }
58
+ const stats = statSync(streamPath);
59
+ if (stats.size === 0) {
60
+ return undefined;
61
+ }
62
+ const readSize = Math.min(stats.size, LOG_TAIL_BYTES);
63
+ const buffer = Buffer.alloc(readSize);
64
+ const fd = openSync(streamPath, "r");
65
+ let bytesRead = 0;
66
+ try {
67
+ bytesRead = readSync(fd, buffer, 0, readSize, stats.size - readSize);
68
+ }
69
+ finally {
70
+ closeSync(fd);
71
+ }
72
+ if (bytesRead <= 0) {
73
+ return undefined;
74
+ }
75
+ const lines = buffer.subarray(0, bytesRead).toString("utf-8").split("\n");
76
+ for (let i = lines.length - 1; i >= 0; i--) {
77
+ const line = lines[i].trim();
78
+ if (!line) {
79
+ continue;
80
+ }
81
+ try {
82
+ const entry = JSON.parse(line);
83
+ if (entry.type === "text") {
84
+ return entry;
85
+ }
86
+ }
87
+ catch {
88
+ // Skip malformed lines (the first line may be partial when reading from a byte offset)
89
+ }
90
+ }
91
+ return undefined;
92
+ }
93
+ /** Read and parse all log entries from a session's JSONL log file. */
94
+ export function readLog(logPath) {
95
+ const streamPath = join(logPath, "stream.jsonl");
96
+ if (!existsSync(streamPath))
97
+ return [];
98
+ const content = readFileSync(streamPath, "utf-8");
99
+ return content
100
+ .split("\n")
101
+ .filter((line) => line.trim())
102
+ .map((line) => JSON.parse(line));
103
+ }
104
+ //# sourceMappingURL=log-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-writer.js","sourceRoot":"","sources":["../src/log-writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type Logger } from "pino";
2
+ /** Application logger for the Grackle server. */
3
+ export declare const logger: Logger;
4
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAEzC,iDAAiD;AACjD,eAAO,MAAM,MAAM,EAAE,MAMnB,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,10 @@
1
+ import pino from "pino";
2
+ /** Application logger for the Grackle server. */
3
+ export const logger = pino({
4
+ name: "grackle-server",
5
+ level: process.env.LOG_LEVEL || "info",
6
+ transport: process.env.NODE_ENV !== "production"
7
+ ? { target: "pino/file", options: { destination: 1 } }
8
+ : undefined,
9
+ });
10
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAqB,MAAM,MAAM,CAAC;AAEzC,iDAAiD;AACjD,MAAM,CAAC,MAAM,MAAM,GAAW,IAAI,CAAC;IACjC,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;IACtC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAC9C,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,EAAE;QACtD,CAAC,CAAC,SAAS;CACd,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Pipe delivery — wires IPC streams to session sendInput for parent↔child communication.
3
+ *
4
+ * - `setupAsyncPipeDelivery()`: registers an async listener that calls sendInput on the
5
+ * parent session when a child publishes a message to the pipe stream.
6
+ * - `publishChildCompletion()`: called from the event processor when a child session with
7
+ * a pipe reaches terminal status, publishing the result to the IPC stream.
8
+ * For async pipes, cleans up after publish. For sync pipes, cleanup is handled
9
+ * by the WaitForPipe consumer after it reads the message.
10
+ */
11
+ /**
12
+ * Register an async delivery listener for a session. When messages arrive on any
13
+ * async subscription for this session, the listener calls sendInput to inject them.
14
+ *
15
+ * Idempotent — safe to call multiple times for the same session.
16
+ *
17
+ * The listener throws if pre-dispatch checks fail (session not found, environment
18
+ * disconnected), which causes the stream-registry to leave the message as undelivered.
19
+ * Note: once sendInput is dispatched, the message is marked delivered even if the
20
+ * gRPC call later fails — delivery tracking covers pre-dispatch failures only.
21
+ */
22
+ export declare function ensureAsyncDeliveryListener(sessionId: string): void;
23
+ /** @deprecated Use ensureAsyncDeliveryListener instead. */
24
+ export declare function setupAsyncPipeDelivery(parentSessionId: string): void;
25
+ /**
26
+ * Publish a completion message to the IPC stream when a child session with a pipe
27
+ * reaches terminal status. Called from the event processor.
28
+ *
29
+ * For async pipes: cleans up the stream and listener immediately after publish.
30
+ * For sync pipes: does NOT clean up — the WaitForPipe consumer handles cleanup
31
+ * after it reads the message (avoids race where cleanup runs before consumer reads).
32
+ */
33
+ export declare function publishChildCompletion(childSessionId: string, status: string): void;
34
+ /**
35
+ * Remove the async listener for a session if it has no remaining async subscriptions.
36
+ * Called from closeFd when closing a pipe fd.
37
+ */
38
+ export declare function cleanupAsyncListenerIfEmpty(sessionId: string): void;
39
+ /** Clear all state. For testing only. */
40
+ export declare function _resetForTesting(): void;
41
+ //# sourceMappingURL=pipe-delivery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipe-delivery.d.ts","sourceRoot":"","sources":["../src/pipe-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAuBH;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAyBnE;AAED,2DAA2D;AAC3D,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAEpE;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAuCnF;AAmED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAUnE;AAED,yCAAyC;AACzC,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Pipe delivery — wires IPC streams to session sendInput for parent↔child communication.
3
+ *
4
+ * - `setupAsyncPipeDelivery()`: registers an async listener that calls sendInput on the
5
+ * parent session when a child publishes a message to the pipe stream.
6
+ * - `publishChildCompletion()`: called from the event processor when a child session with
7
+ * a pipe reaches terminal status, publishing the result to the IPC stream.
8
+ * For async pipes, cleans up after publish. For sync pipes, cleanup is handled
9
+ * by the WaitForPipe consumer after it reads the message.
10
+ */
11
+ import { create } from "@bufbuild/protobuf";
12
+ import { powerline } from "@grackle-ai/common";
13
+ import { sessionStore } from "@grackle-ai/database";
14
+ import * as adapterManager from "./adapter-manager.js";
15
+ import * as streamRegistry from "./stream-registry.js";
16
+ import { readLastTextEntry } from "./log-writer.js";
17
+ import { logger } from "./logger.js";
18
+ /** Maximum length for the child's last text message in pipe delivery. */
19
+ const MAX_LAST_MESSAGE_LENGTH = 4000;
20
+ /** Human-readable status labels keyed by runtime event content strings. */
21
+ const STATUS_LABELS = {
22
+ completed: "completed",
23
+ killed: "was killed",
24
+ failed: "failed",
25
+ };
26
+ /** Stored unsubscribe functions for async listeners, keyed by parent session ID. */
27
+ const asyncListenerCleanups = new Map();
28
+ /**
29
+ * Register an async delivery listener for a session. When messages arrive on any
30
+ * async subscription for this session, the listener calls sendInput to inject them.
31
+ *
32
+ * Idempotent — safe to call multiple times for the same session.
33
+ *
34
+ * The listener throws if pre-dispatch checks fail (session not found, environment
35
+ * disconnected), which causes the stream-registry to leave the message as undelivered.
36
+ * Note: once sendInput is dispatched, the message is marked delivered even if the
37
+ * gRPC call later fails — delivery tracking covers pre-dispatch failures only.
38
+ */
39
+ export function ensureAsyncDeliveryListener(sessionId) {
40
+ if (asyncListenerCleanups.has(sessionId)) {
41
+ return;
42
+ }
43
+ const unsubscribe = streamRegistry.registerAsyncListener(sessionId, (sub, msg) => {
44
+ const session = sessionStore.getSession(sessionId);
45
+ if (!session) {
46
+ throw new Error(`Async pipe delivery: session ${sessionId} not found`);
47
+ }
48
+ const conn = adapterManager.getConnection(session.environmentId);
49
+ if (!conn) {
50
+ throw new Error(`Async pipe delivery: environment ${session.environmentId} not connected`);
51
+ }
52
+ const text = `[fd:${sub.fd}] ${msg.content}`;
53
+ conn.client.sendInput(create(powerline.InputMessageSchema, { sessionId, text })).catch((err) => {
54
+ logger.warn({ err, sessionId }, "Async pipe delivery: sendInput failed after dispatch");
55
+ });
56
+ });
57
+ asyncListenerCleanups.set(sessionId, unsubscribe);
58
+ }
59
+ /** @deprecated Use ensureAsyncDeliveryListener instead. */
60
+ export function setupAsyncPipeDelivery(parentSessionId) {
61
+ ensureAsyncDeliveryListener(parentSessionId);
62
+ }
63
+ /**
64
+ * Publish a completion message to the IPC stream when a child session with a pipe
65
+ * reaches terminal status. Called from the event processor.
66
+ *
67
+ * For async pipes: cleans up the stream and listener immediately after publish.
68
+ * For sync pipes: does NOT clean up — the WaitForPipe consumer handles cleanup
69
+ * after it reads the message (avoids race where cleanup runs before consumer reads).
70
+ */
71
+ export function publishChildCompletion(childSessionId, status) {
72
+ const session = sessionStore.getSession(childSessionId);
73
+ if (!session) {
74
+ return;
75
+ }
76
+ // Only publish for sessions that have a parent and a non-detach pipe
77
+ if (!session.parentSessionId || !session.pipeMode || session.pipeMode === "detach") {
78
+ return;
79
+ }
80
+ // Find the pipe stream by name convention
81
+ const pipeStream = streamRegistry.getStreamByName(`pipe:${childSessionId}`);
82
+ if (!pipeStream) {
83
+ return;
84
+ }
85
+ // Build rich completion message with child's actual output
86
+ const message = buildCompletionMessage(session, status);
87
+ try {
88
+ streamRegistry.publish(pipeStream.id, childSessionId, message);
89
+ }
90
+ catch (err) {
91
+ logger.warn({ err, childSessionId }, "Failed to publish child completion to IPC stream");
92
+ }
93
+ // For async pipes, only clean up if all messages were successfully delivered.
94
+ // If delivery failed (listener threw), keep the stream so hasUndeliveredMessages
95
+ // stays accurate for close() buffer drain checks.
96
+ // For sync pipes, the WaitForPipe handler cleans up after consumeSync returns.
97
+ if (session.pipeMode === "async") {
98
+ // Check all parent subscriptions — if any have undelivered messages, keep the stream
99
+ const parentSubs = streamRegistry.getSubscriptionsForSession(session.parentSessionId)
100
+ .filter((s) => s.streamId === pipeStream.id);
101
+ const allDelivered = parentSubs.every((s) => !streamRegistry.hasUndeliveredMessages(s.id));
102
+ if (allDelivered) {
103
+ cleanupAsyncPipe(pipeStream.id, session.parentSessionId);
104
+ }
105
+ }
106
+ }
107
+ /** Clean up an async pipe stream and its listener (only if no remaining async subs for parent). */
108
+ function cleanupAsyncPipe(streamId, parentSessionId) {
109
+ // Collect all session IDs on this stream before deleting (for listener cleanup)
110
+ const stream = streamRegistry.getStream(streamId);
111
+ const sessionIds = [];
112
+ if (stream) {
113
+ for (const sub of stream.subscriptions.values()) {
114
+ sessionIds.push(sub.sessionId);
115
+ }
116
+ }
117
+ // Delete the stream (which unsubscribes everyone)
118
+ streamRegistry.deleteStream(streamId);
119
+ // Clean up async listeners for all sessions that were on this stream
120
+ // (both parent and child). Only removes if no remaining async subscriptions.
121
+ for (const sid of sessionIds) {
122
+ cleanupAsyncListenerIfEmpty(sid);
123
+ }
124
+ }
125
+ /**
126
+ * Build a rich completion message from the child session's log,
127
+ * including the status and the child's last text output.
128
+ */
129
+ // eslint-disable-next-line @rushstack/no-new-null -- matches DB schema types
130
+ function buildCompletionMessage(session, status) {
131
+ const statusLabel = STATUS_LABELS[status] || status;
132
+ let message = `Child session ${statusLabel}.`;
133
+ // Extract the last text message from the child's session log
134
+ const lastText = extractLastTextMessage(session.logPath || undefined);
135
+ if (lastText) {
136
+ const truncated = lastText.length > MAX_LAST_MESSAGE_LENGTH
137
+ ? lastText.slice(0, MAX_LAST_MESSAGE_LENGTH) + "..."
138
+ : lastText;
139
+ message += `\n\nChild's last message:\n${truncated}`;
140
+ }
141
+ if (session.error) {
142
+ message += `\n\nError: ${session.error}`;
143
+ }
144
+ return message;
145
+ }
146
+ /**
147
+ * Read the session log and extract the content of the last "text" entry.
148
+ * Uses tail-reading to reduce worst-case work on large logs (reads from
149
+ * the end of the file rather than parsing the entire JSONL). Still uses
150
+ * synchronous filesystem calls.
151
+ * Returns an empty string if no text entries exist or the log cannot be read.
152
+ */
153
+ function extractLastTextMessage(logPath) {
154
+ if (!logPath) {
155
+ return "";
156
+ }
157
+ try {
158
+ return readLastTextEntry(logPath)?.content ?? "";
159
+ }
160
+ catch {
161
+ return "";
162
+ }
163
+ }
164
+ /**
165
+ * Remove the async listener for a session if it has no remaining async subscriptions.
166
+ * Called from closeFd when closing a pipe fd.
167
+ */
168
+ export function cleanupAsyncListenerIfEmpty(sessionId) {
169
+ const remainingAsyncSubs = streamRegistry.getSubscriptionsForSession(sessionId)
170
+ .filter((s) => s.deliveryMode === "async");
171
+ if (remainingAsyncSubs.length === 0) {
172
+ const cleanup = asyncListenerCleanups.get(sessionId);
173
+ if (cleanup) {
174
+ cleanup();
175
+ asyncListenerCleanups.delete(sessionId);
176
+ }
177
+ }
178
+ }
179
+ /** Clear all state. For testing only. */
180
+ export function _resetForTesting() {
181
+ for (const cleanup of asyncListenerCleanups.values()) {
182
+ cleanup();
183
+ }
184
+ asyncListenerCleanups.clear();
185
+ }
186
+ //# sourceMappingURL=pipe-delivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipe-delivery.js","sourceRoot":"","sources":["../src/pipe-delivery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /** Mutable task context for a running event processor. */
2
+ export interface ProcessorContext {
3
+ sessionId: string;
4
+ logPath: string;
5
+ workspaceId?: string;
6
+ taskId: string;
7
+ }
8
+ /** Register a processor context for a running event stream. */
9
+ export declare function register(ctx: ProcessorContext): void;
10
+ /** Unregister a processor context when the event stream ends. */
11
+ export declare function unregister(sessionId: string): void;
12
+ /** Retrieve the context for a running event processor, if any. */
13
+ export declare function get(sessionId: string): ProcessorContext | undefined;
14
+ /**
15
+ * Late-bind a task to a running processor. Updates workspaceId, taskId, and onComplete,
16
+ * then fires all registered bind listeners.
17
+ *
18
+ * Idempotent: binding to the same task is a no-op.
19
+ * Throws if the session is already bound to a different task (FR-6).
20
+ * Throws if the session is not registered (not actively processing).
21
+ */
22
+ export declare function lateBind(sessionId: string, taskId: string, workspaceId?: string): void;
23
+ /** Register a callback to be invoked when lateBind is called for this session. */
24
+ export declare function onBind(sessionId: string, listener: () => void): void;
25
+ //# sourceMappingURL=processor-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor-registry.d.ts","sourceRoot":"","sources":["../src/processor-registry.ts"],"names":[],"mappings":"AAEA,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAQD,+DAA+D;AAC/D,wBAAgB,QAAQ,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAEpD;AAED,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGlD;AAED,kEAAkE;AAClE,wBAAgB,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEnE;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CA4BN;AAED,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAOpE"}
@@ -0,0 +1,58 @@
1
+ import { logger } from "./logger.js";
2
+ /** Registry of active event processor contexts, keyed by sessionId. */
3
+ const registry = new Map();
4
+ /** Callbacks invoked when a processor's task context is late-bound. */
5
+ const bindListeners = new Map();
6
+ /** Register a processor context for a running event stream. */
7
+ export function register(ctx) {
8
+ registry.set(ctx.sessionId, ctx);
9
+ }
10
+ /** Unregister a processor context when the event stream ends. */
11
+ export function unregister(sessionId) {
12
+ registry.delete(sessionId);
13
+ bindListeners.delete(sessionId);
14
+ }
15
+ /** Retrieve the context for a running event processor, if any. */
16
+ export function get(sessionId) {
17
+ return registry.get(sessionId);
18
+ }
19
+ /**
20
+ * Late-bind a task to a running processor. Updates workspaceId, taskId, and onComplete,
21
+ * then fires all registered bind listeners.
22
+ *
23
+ * Idempotent: binding to the same task is a no-op.
24
+ * Throws if the session is already bound to a different task (FR-6).
25
+ * Throws if the session is not registered (not actively processing).
26
+ */
27
+ export function lateBind(sessionId, taskId, workspaceId) {
28
+ const ctx = registry.get(sessionId);
29
+ if (!ctx) {
30
+ throw new Error(`No active event processor for session ${sessionId}`);
31
+ }
32
+ if (ctx.taskId !== "" && ctx.taskId !== taskId) {
33
+ throw new Error(`Session ${sessionId} is already bound to task ${ctx.taskId}, cannot rebind to ${taskId}`);
34
+ }
35
+ if (ctx.taskId === taskId) {
36
+ logger.info({ sessionId, taskId }, "Late-bind no-op: session already bound to this task");
37
+ return;
38
+ }
39
+ ctx.workspaceId = workspaceId;
40
+ ctx.taskId = taskId;
41
+ logger.info({ sessionId, taskId, workspaceId }, "Late-bound session to task");
42
+ const listeners = bindListeners.get(sessionId);
43
+ if (listeners) {
44
+ for (const listener of listeners) {
45
+ listener();
46
+ }
47
+ }
48
+ }
49
+ /** Register a callback to be invoked when lateBind is called for this session. */
50
+ export function onBind(sessionId, listener) {
51
+ let listeners = bindListeners.get(sessionId);
52
+ if (!listeners) {
53
+ listeners = [];
54
+ bindListeners.set(sessionId, listeners);
55
+ }
56
+ listeners.push(listener);
57
+ }
58
+ //# sourceMappingURL=processor-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor-registry.js","sourceRoot":"","sources":["../src/processor-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAUrC,uEAAuE;AACvE,MAAM,QAAQ,GAAkC,IAAI,GAAG,EAA4B,CAAC;AAEpF,uEAAuE;AACvE,MAAM,aAAa,GAAmC,IAAI,GAAG,EAA6B,CAAC;AAE3F,+DAA+D;AAC/D,MAAM,UAAU,QAAQ,CAAC,GAAqB;IAC5C,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC3B,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,GAAG,CAAC,SAAiB;IACnC,OAAO,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CACtB,SAAiB,EACjB,MAAc,EACd,WAAoB;IAEpB,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,yCAAyC,SAAS,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,WAAW,SAAS,6BAA6B,GAAG,CAAC,MAAM,sBAAsB,MAAM,EAAE,CAC1F,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,qDAAqD,CAAC,CAAC;QAC1F,OAAO;IACT,CAAC;IAED,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC;IAC9B,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;IAEpB,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,4BAA4B,CAAC,CAAC;IAE9E,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,QAAQ,EAAE,CAAC;QACb,CAAC;IACH,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,MAAM,CAAC,SAAiB,EAAE,QAAoB;IAC5D,IAAI,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,EAAE,CAAC;QACf,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IACD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { SessionRow } from "@grackle-ai/database";
2
+ /**
3
+ * Reanimate a terminal session: validate state, reset the DB record, and fire a
4
+ * PowerLine resume stream. Returns the updated session row (status=running).
5
+ *
6
+ * Throws ConnectError on any validation failure:
7
+ * - NOT_FOUND if the session does not exist
8
+ * - FAILED_PRECONDITION if the session is still active, has no runtimeSessionId,
9
+ * the environment already has an active session, or the environment is offline
10
+ */
11
+ export declare function reanimateAgent(sessionId: string): SessionRow;
12
+ //# sourceMappingURL=reanimate-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reanimate-agent.d.ts","sourceRoot":"","sources":["../src/reanimate-agent.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIvD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAqF5D"}
@@ -0,0 +1,76 @@
1
+ import { ConnectError, Code } from "@connectrpc/connect";
2
+ import { create } from "@bufbuild/protobuf";
3
+ import { powerline, SESSION_STATUS, LOGS_DIR } from "@grackle-ai/common";
4
+ import { join } from "node:path";
5
+ import { sessionStore, taskStore, grackleHome } from "@grackle-ai/database";
6
+ import * as adapterManager from "./adapter-manager.js";
7
+ import { processEventStream } from "./event-processor.js";
8
+ /**
9
+ * Reanimate a terminal session: validate state, reset the DB record, and fire a
10
+ * PowerLine resume stream. Returns the updated session row (status=running).
11
+ *
12
+ * Throws ConnectError on any validation failure:
13
+ * - NOT_FOUND if the session does not exist
14
+ * - FAILED_PRECONDITION if the session is still active, has no runtimeSessionId,
15
+ * the environment already has an active session, or the environment is offline
16
+ */
17
+ export function reanimateAgent(sessionId) {
18
+ const session = sessionStore.getSession(sessionId);
19
+ if (!session) {
20
+ throw new ConnectError(`Session not found: ${sessionId}`, Code.NotFound);
21
+ }
22
+ if (session.status === SESSION_STATUS.IDLE ||
23
+ session.status === SESSION_STATUS.RUNNING ||
24
+ session.status === SESSION_STATUS.PENDING) {
25
+ throw new ConnectError(`Session ${sessionId} is already active (status: ${session.status})`, Code.FailedPrecondition);
26
+ }
27
+ if (!session.runtimeSessionId) {
28
+ throw new ConnectError(`Session ${sessionId} has no runtime session ID — cannot reanimate`, Code.FailedPrecondition);
29
+ }
30
+ const existingActive = sessionStore.getActiveForEnv(session.environmentId);
31
+ if (existingActive) {
32
+ throw new ConnectError(`Environment already has active session ${existingActive.id}`, Code.FailedPrecondition);
33
+ }
34
+ // Note: the check above and reanimateSession() below are not wrapped in a DB
35
+ // transaction, but Node.js's single-threaded event loop provides sufficient
36
+ // serialization: this function is fully synchronous (no awaits, all SQLite
37
+ // calls use the synchronous better-sqlite3 API), so it runs to completion
38
+ // before any other handler can interleave.
39
+ const conn = adapterManager.getConnection(session.environmentId);
40
+ if (!conn) {
41
+ throw new ConnectError(`Environment ${session.environmentId} not connected`, Code.FailedPrecondition);
42
+ }
43
+ const powerlineReq = create(powerline.ResumeRequestSchema, {
44
+ sessionId: session.id,
45
+ runtimeSessionId: session.runtimeSessionId,
46
+ runtime: session.runtime,
47
+ });
48
+ const logPath = session.logPath || join(grackleHome, LOGS_DIR, session.id);
49
+ let workspaceId;
50
+ let taskId;
51
+ if (session.taskId) {
52
+ const task = taskStore.getTask(session.taskId);
53
+ if (task) {
54
+ workspaceId = task.workspaceId || undefined;
55
+ taskId = task.id;
56
+ }
57
+ }
58
+ // Initiate the stream before mutating the DB. If resume() throws synchronously
59
+ // the DB is never touched, so no rollback is needed.
60
+ let resumeStream;
61
+ try {
62
+ resumeStream = conn.client.resume(powerlineReq);
63
+ }
64
+ catch (err) {
65
+ throw new ConnectError(`Failed to initiate resume stream: ${String(err)}`, Code.Internal);
66
+ }
67
+ sessionStore.reanimateSession(session.id);
68
+ processEventStream(resumeStream, {
69
+ sessionId: session.id,
70
+ logPath,
71
+ workspaceId,
72
+ taskId,
73
+ });
74
+ return sessionStore.getSession(session.id);
75
+ }
76
+ //# sourceMappingURL=reanimate-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reanimate-agent.js","sourceRoot":"","sources":["../src/reanimate-agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { PowerLineConnection } from "@grackle-ai/adapter-sdk";
2
+ /**
3
+ * Recover disconnected sessions for a newly reconnected environment.
4
+ *
5
+ * Finds sessions in SUSPENDED, RUNNING, or IDLE state (RUNNING/IDLE handles
6
+ * the "server died" scenario where sessions never got suspended). Drains
7
+ * buffered events from PowerLine, writes them to the session JSONL, then
8
+ * reanimates the first recoverable session. Remaining sessions are left
9
+ * SUSPENDED for later recovery (only one active session per environment).
10
+ *
11
+ * Fire-and-forget: logs errors but does not throw.
12
+ */
13
+ export declare function recoverSuspendedSessions(environmentId: string, connection: PowerLineConnection): Promise<void>;
14
+ /** @internal Reset the recovery lock for testing. */
15
+ export declare function _resetForTesting(): void;
16
+ //# sourceMappingURL=session-recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-recovery.d.ts","sourceRoot":"","sources":["../src/session-recovery.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAWnE;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,mBAAmB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAkHf;AAWD,qDAAqD;AACrD,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}