@someonegg/agent-hub 0.0.0-stage → 0.7.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.
Files changed (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +104 -2
  3. package/README.zh-CN.md +99 -0
  4. package/README.zh-TW.md +99 -0
  5. package/dist/client/apple-touch-icon.png +0 -0
  6. package/dist/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  7. package/dist/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  8. package/dist/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  9. package/dist/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  10. package/dist/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  11. package/dist/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  12. package/dist/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  13. package/dist/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  14. package/dist/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  15. package/dist/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  16. package/dist/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  17. package/dist/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  18. package/dist/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  19. package/dist/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  20. package/dist/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  21. package/dist/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  22. package/dist/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  23. package/dist/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  24. package/dist/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  25. package/dist/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  26. package/dist/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  27. package/dist/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  28. package/dist/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  29. package/dist/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  30. package/dist/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  31. package/dist/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  32. package/dist/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  33. package/dist/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  34. package/dist/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  35. package/dist/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  36. package/dist/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  37. package/dist/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  38. package/dist/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  39. package/dist/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  40. package/dist/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  41. package/dist/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  42. package/dist/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  43. package/dist/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  44. package/dist/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  45. package/dist/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  46. package/dist/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  47. package/dist/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  48. package/dist/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  49. package/dist/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  50. package/dist/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  51. package/dist/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  52. package/dist/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  53. package/dist/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  54. package/dist/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  55. package/dist/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  56. package/dist/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  57. package/dist/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  58. package/dist/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  59. package/dist/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  60. package/dist/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  61. package/dist/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  62. package/dist/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  63. package/dist/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  64. package/dist/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  65. package/dist/client/assets/index-4ejyszaV.css +1 -0
  66. package/dist/client/assets/index-CKKT4cMK.js +309 -0
  67. package/dist/client/favicon.ico +0 -0
  68. package/dist/client/favicon.svg +6 -0
  69. package/dist/client/icons/icon-192.png +0 -0
  70. package/dist/client/icons/icon-512.png +0 -0
  71. package/dist/client/icons/icon-maskable-512.png +0 -0
  72. package/dist/client/index.html +22 -0
  73. package/dist/client/manifest.webmanifest +21 -0
  74. package/dist/server/server/adapters/claude/claude-session-source.js +220 -0
  75. package/dist/server/server/adapters/claude/identity.js +28 -0
  76. package/dist/server/server/adapters/claude/interaction.js +68 -0
  77. package/dist/server/server/adapters/claude/prompt-readiness.js +12 -0
  78. package/dist/server/server/adapters/claude/ready-script.js +25 -0
  79. package/dist/server/server/adapters/claude/session-manager.js +106 -0
  80. package/dist/server/server/adapters/claude/session-normalizer.js +173 -0
  81. package/dist/server/server/adapters/claude/user-input-normalizer.js +71 -0
  82. package/dist/server/server/adapters/codex/catalog-source.js +2 -0
  83. package/dist/server/server/adapters/codex/codex-session-source.js +250 -0
  84. package/dist/server/server/adapters/codex/identity-resolver.js +104 -0
  85. package/dist/server/server/adapters/codex/interaction-parser.js +70 -0
  86. package/dist/server/server/adapters/codex/io-errors.js +2 -0
  87. package/dist/server/server/adapters/codex/jsonl-catalog-source.js +84 -0
  88. package/dist/server/server/adapters/codex/limits.js +2 -0
  89. package/dist/server/server/adapters/codex/message-normalizer.js +66 -0
  90. package/dist/server/server/adapters/codex/response-item-parser.js +64 -0
  91. package/dist/server/server/adapters/codex/rollout-descriptor.js +2 -0
  92. package/dist/server/server/adapters/codex/session-index-reader.js +101 -0
  93. package/dist/server/server/adapters/codex/session-manager.js +51 -0
  94. package/dist/server/server/adapters/codex/session-normalizer.js +175 -0
  95. package/dist/server/server/adapters/codex/subagent-identity.js +9 -0
  96. package/dist/server/server/adapters/codex/user-input-normalizer.js +121 -0
  97. package/dist/server/server/adapters/codex/value-readers.js +4 -0
  98. package/dist/server/server/adapters/shared/binding-protocol.js +54 -0
  99. package/dist/server/server/adapters/shared/io-errors.js +13 -0
  100. package/dist/server/server/adapters/shared/jsonl-decoder.js +263 -0
  101. package/dist/server/server/adapters/shared/limits.js +4 -0
  102. package/dist/server/server/adapters/shared/session-management.js +43 -0
  103. package/dist/server/server/adapters/shared/source-cache.js +23 -0
  104. package/dist/server/server/api/session-api-mapper.js +65 -0
  105. package/dist/server/server/application/interaction-session-reader.js +17 -0
  106. package/dist/server/server/application/session-read-service.js +86 -0
  107. package/dist/server/server/application/session-reader.js +2 -0
  108. package/dist/server/server/application/session-response.js +11 -0
  109. package/dist/server/server/catalog/config.js +4 -0
  110. package/dist/server/server/catalog/session-catalog-service.js +112 -0
  111. package/dist/server/server/cli/command-error.js +27 -0
  112. package/dist/server/server/cli/main.js +42 -0
  113. package/dist/server/server/cli/output.js +52 -0
  114. package/dist/server/server/cli/resource.js +104 -0
  115. package/dist/server/server/cli/session-client.js +64 -0
  116. package/dist/server/server/cli/session-follow.js +58 -0
  117. package/dist/server/server/cli/session-output.js +184 -0
  118. package/dist/server/server/cli/session-wait.js +44 -0
  119. package/dist/server/server/cli/session.js +318 -0
  120. package/dist/server/server/cli/skill.js +37 -0
  121. package/dist/server/server/config.js +196 -0
  122. package/dist/server/server/create-hub.js +91 -0
  123. package/dist/server/server/directory-lock.js +65 -0
  124. package/dist/server/server/domain/project-directory.js +5 -0
  125. package/dist/server/server/domain/session-domain.js +2 -0
  126. package/dist/server/server/domain/session-text.js +45 -0
  127. package/dist/server/server/hosting/diff-store.js +103 -0
  128. package/dist/server/server/hosting/routes.js +168 -0
  129. package/dist/server/server/hosting/store.js +162 -0
  130. package/dist/server/server/hosting/validation-error.js +4 -0
  131. package/dist/server/server/http/api-route-handler.js +2 -0
  132. package/dist/server/server/http/api-router.js +54 -0
  133. package/dist/server/server/http/create-server.js +218 -0
  134. package/dist/server/server/http/query-params.js +19 -0
  135. package/dist/server/server/http/request-body.js +48 -0
  136. package/dist/server/server/http/request-errors.js +59 -0
  137. package/dist/server/server/http/router.js +16 -0
  138. package/dist/server/server/http/security.js +19 -0
  139. package/dist/server/server/http/session-interaction-routes.js +128 -0
  140. package/dist/server/server/http/session-management-routes.js +105 -0
  141. package/dist/server/server/http/session-path.js +10 -0
  142. package/dist/server/server/http/session-read-routes.js +143 -0
  143. package/dist/server/server/http/terminal-preview-websocket.js +179 -0
  144. package/dist/server/server/http/upload-routes.js +124 -0
  145. package/dist/server/server/interaction/interaction-service.js +276 -0
  146. package/dist/server/server/interaction/message.js +19 -0
  147. package/dist/server/server/interaction/pane-binding-manager.js +62 -0
  148. package/dist/server/server/interaction/pane-observation.js +31 -0
  149. package/dist/server/server/interaction/reconcile-session-state.js +21 -0
  150. package/dist/server/server/interaction/runtime-capabilities.js +2 -0
  151. package/dist/server/server/interaction/session-runtime.js +86 -0
  152. package/dist/server/server/interaction/terminal-activity-service.js +81 -0
  153. package/dist/server/server/interaction/terminal-preview-diff.js +71 -0
  154. package/dist/server/server/interaction/terminal-preview-service.js +184 -0
  155. package/dist/server/server/interaction/tmux-launcher.js +84 -0
  156. package/dist/server/server/interaction/tmux-preview-listener.js +107 -0
  157. package/dist/server/server/interaction/tmux-service.js +334 -0
  158. package/dist/server/server/interaction/tmux-target.js +13 -0
  159. package/dist/server/server/interaction/transport.js +9 -0
  160. package/dist/server/server/main.js +54 -0
  161. package/dist/server/server/management/operation-service.js +307 -0
  162. package/dist/server/server/management/operation-state.js +3 -0
  163. package/dist/server/server/management/session-exit-service.js +50 -0
  164. package/dist/server/server/management/session-launch-executor.js +164 -0
  165. package/dist/server/server/management/session-lifecycle-executor.js +158 -0
  166. package/dist/server/server/management/session-manager.js +64 -0
  167. package/dist/server/server/repository/catalog-snapshot-store.js +318 -0
  168. package/dist/server/server/repository/diagnostic-logger.js +70 -0
  169. package/dist/server/server/repository/refresh-coordinator.js +14 -0
  170. package/dist/server/server/repository/session-queries.js +72 -0
  171. package/dist/server/server/security/opaque-id.js +17 -0
  172. package/dist/server/server/source/session-source.js +2 -0
  173. package/dist/server/server/updates/session-updates-service.js +151 -0
  174. package/dist/server/server/updates/update-revision.js +22 -0
  175. package/dist/server/server/uploads/store.js +304 -0
  176. package/dist/server/server/version.js +21 -0
  177. package/dist/server/shared/api-contract.js +2 -0
  178. package/dist/server/shared/diff.js +158 -0
  179. package/dist/server/shared/domain.js +2 -0
  180. package/dist/server/shared/interaction.js +22 -0
  181. package/dist/server/shared/task-name.js +6 -0
  182. package/dist/server/shared/uploads.js +8 -0
  183. package/package.json +98 -4
  184. package/skills/ahub-control/SKILL.md +93 -0
  185. package/skills/ahub-publish/SKILL.md +54 -0
@@ -0,0 +1,4 @@
1
+ export function nonEmptyString(value) {
2
+ return typeof value === "string" && value.length > 0 ? value : null;
3
+ }
4
+ //# sourceMappingURL=value-readers.js.map
@@ -0,0 +1,54 @@
1
+ import { isAbsolute } from "node:path";
2
+ import { validTaskName } from "../../../shared/task-name.js";
3
+ import { validProjectDirectory } from "../../domain/project-directory.js";
4
+ import { validTmuxSessionName } from "../../interaction/tmux-target.js";
5
+ /** Parse protocol fields only after the adapter has established native provenance. */
6
+ export function bindingAttempts(lines, ordinal, validOperationId) {
7
+ const found = [];
8
+ for (let index = 0; index < lines.length; index++) {
9
+ if (lines[index] !== "AHUB_TMUX_BIND_V3")
10
+ continue;
11
+ const socketPath = lines[index + 1] ?? "";
12
+ const sessionName = lines[index + 2] ?? "";
13
+ const operationId = lines[index + 3] ?? "";
14
+ const projectDirectory = lines[index + 4];
15
+ let taskName;
16
+ try {
17
+ const value = JSON.parse(lines[index + 5] ?? "");
18
+ if (validTaskName(value))
19
+ taskName = value.trim() || undefined;
20
+ }
21
+ catch {
22
+ /* Optional metadata cannot invalidate a binding. */
23
+ }
24
+ if (!isAbsolute(socketPath) ||
25
+ !validTmuxSessionName(sessionName) ||
26
+ (operationId && !validOperationId(operationId))) {
27
+ found.push({ ordinal, valid: false });
28
+ continue;
29
+ }
30
+ found.push({
31
+ ordinal,
32
+ valid: true,
33
+ socketPath,
34
+ sessionName,
35
+ ...(operationId ? { operationId } : {}),
36
+ ...(validProjectDirectory(projectDirectory) ? { projectDirectory } : {}),
37
+ ...(taskName ? { taskName } : {}),
38
+ });
39
+ }
40
+ return found;
41
+ }
42
+ export function mergeBindingMetadata(previous, attempts) {
43
+ let { projectDirectory, taskName } = previous;
44
+ for (const attempt of attempts) {
45
+ if (!attempt.valid)
46
+ continue;
47
+ if (validProjectDirectory(attempt.projectDirectory))
48
+ projectDirectory = attempt.projectDirectory;
49
+ if (validTaskName(attempt.taskName) && attempt.taskName.trim())
50
+ taskName = attempt.taskName.trim();
51
+ }
52
+ return { projectDirectory, taskName };
53
+ }
54
+ //# sourceMappingURL=binding-protocol.js.map
@@ -0,0 +1,13 @@
1
+ const EXPECTED_IO_ERRORS = new Set([
2
+ "ENOENT",
3
+ "EACCES",
4
+ "EPERM",
5
+ "ESTALE",
6
+ "EISDIR",
7
+ "ENOTDIR",
8
+ "ELOOP",
9
+ ]);
10
+ export function isExpectedIoError(error) {
11
+ return error instanceof Error && "code" in error && EXPECTED_IO_ERRORS.has(String(error.code));
12
+ }
13
+ //# sourceMappingURL=io-errors.js.map
@@ -0,0 +1,263 @@
1
+ import { createHash } from "node:crypto";
2
+ import { open } from "node:fs/promises";
3
+ import { MAX_JSONL_LINE_BYTES, MAX_SESSION_DIAGNOSTICS } from "./limits.js";
4
+ export const PROBE_BYTES = 4 * 1024;
5
+ export class CheckpointedJsonlDecoder {
6
+ async decode(descriptor, checkpoint) {
7
+ const handle = await open(descriptor.path, "r");
8
+ try {
9
+ const info = await handle.stat();
10
+ const observedEof = info.size;
11
+ let probeBytes = 0;
12
+ let appendCheckpoint = null;
13
+ let validatedWindows = [];
14
+ if (isCompatibleCheckpoint(checkpoint, observedEof)) {
15
+ const validation = await validateProbes(handle, checkpoint);
16
+ probeBytes += validation.bytesRead;
17
+ if (validation.matches) {
18
+ appendCheckpoint = checkpoint;
19
+ validatedWindows = validation.windows;
20
+ }
21
+ }
22
+ const start = appendCheckpoint?.committedOffset ?? 0;
23
+ const baseOrdinal = appendCheckpoint?.physicalLineCount ?? 0;
24
+ const priorDiagnostics = appendCheckpoint?.diagnostics ?? [];
25
+ const decoded = await decodeRange(handle, start, observedEof, baseOrdinal, priorDiagnostics);
26
+ const probes = createProbes(observedEof, [
27
+ ...validatedWindows,
28
+ { offset: start, contents: decoded.headBytes },
29
+ { offset: decoded.tailOffset, contents: decoded.tailBytes },
30
+ ]);
31
+ const checkpointDiagnostics = decoded.diagnostics.map(cloneDiagnostic);
32
+ const nextCheckpoint = {
33
+ observedEof,
34
+ committedOffset: decoded.committedOffset,
35
+ physicalLineCount: decoded.physicalLineCount,
36
+ diagnostics: checkpointDiagnostics,
37
+ headProbe: probes.head,
38
+ tailProbe: probes.tail,
39
+ };
40
+ return {
41
+ descriptor,
42
+ records: decoded.records,
43
+ diagnostics: checkpointDiagnostics.map(cloneDiagnostic),
44
+ batchDiagnostics: decoded.batchDiagnostics.map(cloneDiagnostic),
45
+ mode: appendCheckpoint === null ? "full" : "append",
46
+ checkpoint: nextCheckpoint,
47
+ telemetry: {
48
+ probeBytes,
49
+ decodeBytes: decoded.bytesRead,
50
+ totalBytes: probeBytes + decoded.bytesRead,
51
+ },
52
+ };
53
+ }
54
+ finally {
55
+ await handle.close();
56
+ }
57
+ }
58
+ }
59
+ async function decodeRange(handle, start, observedEof, baseOrdinal, priorDiagnostics) {
60
+ const records = [];
61
+ const diagnostics = priorDiagnostics.slice(0, MAX_SESSION_DIAGNOSTICS).map(cloneDiagnostic);
62
+ const batchDiagnostics = [];
63
+ const lineChunks = [];
64
+ let lineBytes = 0;
65
+ let ordinal = baseOrdinal;
66
+ let committedOffset = start;
67
+ let bytesRead = 0;
68
+ let droppingOversizedLine = false;
69
+ const headParts = [];
70
+ let headLength = 0;
71
+ let tailBytes = Buffer.alloc(0);
72
+ if (start < observedEof) {
73
+ const stream = handle.createReadStream({
74
+ autoClose: false,
75
+ start,
76
+ end: observedEof - 1,
77
+ });
78
+ for await (const rawChunk of stream) {
79
+ const chunk = rawChunk;
80
+ bytesRead += chunk.length;
81
+ if (headLength < PROBE_BYTES) {
82
+ const part = chunk.subarray(0, PROBE_BYTES - headLength);
83
+ headParts.push(part);
84
+ headLength += part.length;
85
+ }
86
+ tailBytes = tailWindow(tailBytes, chunk);
87
+ let cursor = 0;
88
+ while (cursor < chunk.length) {
89
+ const newline = chunk.indexOf(0x0a, cursor);
90
+ const end = newline < 0 ? chunk.length : newline;
91
+ if (!droppingOversizedLine && end > cursor) {
92
+ const part = chunk.subarray(cursor, end);
93
+ lineBytes += part.length;
94
+ // Keep one possible trailing CR until the newline decides whether it
95
+ // is part of a CRLF terminator. decodeLine applies the final limit
96
+ // after stripping that CR.
97
+ if (lineBytes > MAX_JSONL_LINE_BYTES + 1) {
98
+ lineChunks.length = 0;
99
+ droppingOversizedLine = true;
100
+ }
101
+ else {
102
+ lineChunks.push(part);
103
+ }
104
+ }
105
+ if (newline < 0)
106
+ break;
107
+ ordinal += 1;
108
+ committedOffset = start + bytesRead - (chunk.length - newline - 1);
109
+ if (droppingOversizedLine) {
110
+ retainDiagnostic(diagnostics, batchDiagnostics, lineTooLargeDiagnostic(ordinal));
111
+ }
112
+ else {
113
+ decodeLine(lineBuffer(lineChunks, lineBytes), ordinal, records, diagnostics, batchDiagnostics);
114
+ }
115
+ lineChunks.length = 0;
116
+ lineBytes = 0;
117
+ droppingOversizedLine = false;
118
+ cursor = newline + 1;
119
+ }
120
+ }
121
+ }
122
+ return {
123
+ records,
124
+ diagnostics,
125
+ batchDiagnostics,
126
+ committedOffset,
127
+ physicalLineCount: ordinal,
128
+ bytesRead,
129
+ headBytes: Buffer.concat(headParts, headLength),
130
+ tailBytes,
131
+ tailOffset: observedEof - tailBytes.length,
132
+ };
133
+ }
134
+ function lineBuffer(chunks, length) {
135
+ if (chunks.length === 0)
136
+ return Buffer.alloc(0);
137
+ if (chunks.length === 1)
138
+ return chunks[0];
139
+ return Buffer.concat(chunks, length);
140
+ }
141
+ function decodeLine(rawLine, ordinal, records, diagnostics, batchDiagnostics) {
142
+ const line = rawLine.at(-1) === 0x0d ? rawLine.subarray(0, -1) : rawLine;
143
+ if (line.length === 0)
144
+ return;
145
+ if (line.length > MAX_JSONL_LINE_BYTES) {
146
+ retainDiagnostic(diagnostics, batchDiagnostics, lineTooLargeDiagnostic(ordinal));
147
+ return;
148
+ }
149
+ try {
150
+ const value = JSON.parse(line.toString("utf8"));
151
+ if (!isObject(value)) {
152
+ retainDiagnostic(diagnostics, batchDiagnostics, {
153
+ code: "invalid_record",
154
+ severity: "warning",
155
+ message: "A JSONL line was not a JSON object and was skipped.",
156
+ ordinal,
157
+ });
158
+ return;
159
+ }
160
+ records.push({ ordinal, value });
161
+ }
162
+ catch {
163
+ retainDiagnostic(diagnostics, batchDiagnostics, {
164
+ code: "malformed_json",
165
+ severity: "warning",
166
+ message: "A malformed JSONL line was skipped.",
167
+ ordinal,
168
+ });
169
+ }
170
+ }
171
+ function lineTooLargeDiagnostic(ordinal) {
172
+ return {
173
+ code: "line_too_large",
174
+ severity: "warning",
175
+ message: "A JSONL record exceeded the decode limit and was skipped.",
176
+ ordinal,
177
+ };
178
+ }
179
+ function retainDiagnostic(diagnostics, batchDiagnostics, diagnostic) {
180
+ if (diagnostics.length < MAX_SESSION_DIAGNOSTICS) {
181
+ diagnostics.push(cloneDiagnostic(diagnostic));
182
+ batchDiagnostics.push(cloneDiagnostic(diagnostic));
183
+ }
184
+ }
185
+ function isCompatibleCheckpoint(checkpoint, observedEof) {
186
+ return checkpoint !== undefined && observedEof > checkpoint.observedEof;
187
+ }
188
+ async function validateProbes(handle, checkpoint) {
189
+ const head = await readProbe(handle, checkpoint.headProbe);
190
+ const tail = await readProbe(handle, checkpoint.tailProbe);
191
+ return {
192
+ matches: head.matches && tail.matches,
193
+ bytesRead: head.bytesRead + tail.bytesRead,
194
+ windows: [
195
+ { offset: checkpoint.headProbe.offset, contents: head.contents },
196
+ { offset: checkpoint.tailProbe.offset, contents: tail.contents },
197
+ ],
198
+ };
199
+ }
200
+ function createProbes(observedEof, windows) {
201
+ const headLength = Math.min(PROBE_BYTES, observedEof);
202
+ const tailOffset = Math.max(0, observedEof - PROBE_BYTES);
203
+ return {
204
+ head: probeFromWindows(0, headLength, windows),
205
+ tail: probeFromWindows(tailOffset, observedEof - tailOffset, windows),
206
+ };
207
+ }
208
+ async function probeAt(handle, offset, length) {
209
+ const buffer = Buffer.alloc(length);
210
+ let bytesRead = 0;
211
+ if (length > 0) {
212
+ ({ bytesRead } = await handle.read(buffer, 0, length, offset));
213
+ }
214
+ const contents = buffer.subarray(0, bytesRead);
215
+ return {
216
+ probe: {
217
+ offset,
218
+ length: bytesRead,
219
+ sha256: sha256(contents),
220
+ },
221
+ bytesRead,
222
+ contents,
223
+ };
224
+ }
225
+ async function readProbe(handle, expected) {
226
+ const actual = await probeAt(handle, expected.offset, expected.length);
227
+ const contents = actual.contents;
228
+ return {
229
+ matches: actual.probe.length === expected.length && actual.probe.sha256 === expected.sha256,
230
+ bytesRead: actual.bytesRead,
231
+ contents,
232
+ };
233
+ }
234
+ function sha256(value) {
235
+ return createHash("sha256").update(value).digest("hex");
236
+ }
237
+ function tailWindow(previous, chunk) {
238
+ if (chunk.length >= PROBE_BYTES)
239
+ return chunk.subarray(chunk.length - PROBE_BYTES);
240
+ const keep = Math.min(previous.length, PROBE_BYTES - chunk.length);
241
+ return Buffer.concat([previous.subarray(previous.length - keep), chunk], keep + chunk.length);
242
+ }
243
+ function probeFromWindows(offset, length, windows) {
244
+ const contents = Buffer.alloc(length);
245
+ let cursor = offset;
246
+ while (cursor < offset + length) {
247
+ const window = windows.find(({ offset: windowOffset, contents: value }) => cursor >= windowOffset && cursor < windowOffset + value.length);
248
+ if (window === undefined) {
249
+ throw new Error("Decoder probe bytes were not retained");
250
+ }
251
+ const sourceStart = cursor - window.offset;
252
+ const copied = window.contents.copy(contents, cursor - offset, sourceStart, Math.min(window.contents.length, sourceStart + offset + length - cursor));
253
+ cursor += copied;
254
+ }
255
+ return { offset, length, sha256: sha256(contents) };
256
+ }
257
+ function cloneDiagnostic(value) {
258
+ return { ...value };
259
+ }
260
+ export function isObject(value) {
261
+ return typeof value === "object" && value !== null && !Array.isArray(value);
262
+ }
263
+ //# sourceMappingURL=jsonl-decoder.js.map
@@ -0,0 +1,4 @@
1
+ export const MAX_JSONL_LINE_BYTES = 8 * 1024 * 1024;
2
+ export const MAX_SESSION_DIAGNOSTICS = 50;
3
+ export const MAX_MESSAGE_CHARS = 1_000_000;
4
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1,43 @@
1
+ import { realpath, stat } from "node:fs/promises";
2
+ import { validProjectDirectory } from "../../domain/project-directory.js";
3
+ export async function resolveDirectory(input) {
4
+ if (!validProjectDirectory(input))
5
+ throw new Error("Directory must be an absolute local path");
6
+ const directory = await realpath(input);
7
+ if (!(await stat(directory)).isDirectory())
8
+ throw Object.assign(new Error("Path is not a directory"), { code: "ENOTDIR" });
9
+ return { directory };
10
+ }
11
+ export const DEFAULT_KEY_BINDINGS = {
12
+ enter: "Enter",
13
+ up: "Up",
14
+ down: "Down",
15
+ left: "Left",
16
+ right: "Right",
17
+ BTab: "BTab",
18
+ "0": "0",
19
+ "1": "1",
20
+ "2": "2",
21
+ "3": "3",
22
+ "4": "4",
23
+ "5": "5",
24
+ "6": "6",
25
+ "7": "7",
26
+ "8": "8",
27
+ "9": "9",
28
+ };
29
+ export function interruptSteps() {
30
+ return [
31
+ { keys: ["C-u"], delayMs: 0 },
32
+ { keys: ["Escape"], delayMs: 100 },
33
+ ];
34
+ }
35
+ export function exitSteps() {
36
+ return [
37
+ { keys: ["C-c"], delayMs: 0 },
38
+ { keys: ["C-c"], delayMs: 200 },
39
+ { keys: ["C-c"], delayMs: 2400 },
40
+ { keys: ["C-c"], delayMs: 200 },
41
+ ];
42
+ }
43
+ //# sourceMappingURL=session-management.js.map
@@ -0,0 +1,23 @@
1
+ export function fingerprintOf(value) {
2
+ return { sourceRelativePath: value.sourceRelativePath, size: value.size, mtimeMs: value.mtimeMs };
3
+ }
4
+ export function sameFingerprint(left, right) {
5
+ return (left.sourceRelativePath === right.sourceRelativePath &&
6
+ left.size === right.size &&
7
+ left.mtimeMs === right.mtimeMs);
8
+ }
9
+ export function mutableTelemetry() {
10
+ return {
11
+ fullFiles: 0,
12
+ appendFiles: 0,
13
+ probeBytes: 0,
14
+ decodeBytes: 0,
15
+ decodeMs: 0,
16
+ normalizeMs: 0,
17
+ };
18
+ }
19
+ /** Accepted paths win; logical path order resolves initial and replacement ties. */
20
+ export function acceptedPathOrder(accepted, left, right) {
21
+ return Number(accepted.has(right)) - Number(accepted.has(left)) || left.localeCompare(right);
22
+ }
23
+ //# sourceMappingURL=source-cache.js.map
@@ -0,0 +1,65 @@
1
+ import { createHash } from "node:crypto";
2
+ export class SessionApiMapper {
3
+ resolveDisplayTitle;
4
+ agents;
5
+ constructor(resolveDisplayTitle = () => null, agents = []) {
6
+ this.resolveDisplayTitle = resolveDisplayTitle;
7
+ this.agents = agents;
8
+ }
9
+ list(sessions, effectiveStates) {
10
+ const projectDirectories = new Set();
11
+ const summaries = sessions
12
+ .map((session) => {
13
+ const projectDirectory = session.projectDirectory;
14
+ const isChild = session.isChild ?? session.parentId !== null;
15
+ if (!isChild && projectDirectory !== null)
16
+ projectDirectories.add(projectDirectory);
17
+ return {
18
+ id: session.id,
19
+ agent: session.origin.agent,
20
+ title: this.resolveDisplayTitle(session) ?? session.title,
21
+ projectDirectory,
22
+ ...(session.taskName ? { taskName: session.taskName } : {}),
23
+ status: effectiveStates?.get(session.id) ??
24
+ (session.archived ? "archived" : "normal"),
25
+ parentId: session.parentId,
26
+ isChild,
27
+ updatedAt: session.updatedAt,
28
+ };
29
+ })
30
+ .sort((a, b) => compareIds(a.id, b.id));
31
+ const structure = {
32
+ agents: [...this.agents],
33
+ projects: [...projectDirectories].sort(compareIds).map((directory) => ({ directory })),
34
+ sessions: summaries.map(({ updatedAt, ...summary }) => summary),
35
+ };
36
+ return {
37
+ revision: createHash("sha256").update(JSON.stringify(structure)).digest("base64url"),
38
+ agents: structure.agents,
39
+ projects: structure.projects,
40
+ sessions: summaries,
41
+ };
42
+ }
43
+ sessionDetail(session) {
44
+ return {
45
+ resume: { allowed: false, reason: "Runtime validation unavailable" },
46
+ id: session.id,
47
+ origin: { ...session.origin },
48
+ title: this.resolveDisplayTitle(session) ?? session.title,
49
+ workingDirectory: session.workingDirectory,
50
+ projectDirectory: session.projectDirectory,
51
+ ...(session.taskName ? { taskName: session.taskName } : {}),
52
+ createdAt: session.createdAt,
53
+ updatedAt: session.updatedAt,
54
+ status: session.archived ? "archived" : "normal",
55
+ parentId: session.parentId,
56
+ childIds: [...session.childIds],
57
+ subagent: session.subagent === null ? null : { ...session.subagent },
58
+ sourceId: session.sourceId,
59
+ };
60
+ }
61
+ }
62
+ function compareIds(a, b) {
63
+ return a < b ? -1 : a > b ? 1 : 0;
64
+ }
65
+ //# sourceMappingURL=session-api-mapper.js.map
@@ -0,0 +1,17 @@
1
+ /** Read internal interaction facts without assembling public session responses. */
2
+ export class CatalogInteractionSessionReader {
3
+ store;
4
+ constructor(store) {
5
+ this.store = store;
6
+ }
7
+ async getInteractionSession(id) {
8
+ const normalized = (await this.store.current()).sessions.get(id);
9
+ return normalized === undefined
10
+ ? null
11
+ : {
12
+ archived: normalized.session.archived,
13
+ interaction: normalized.interaction ?? null,
14
+ };
15
+ }
16
+ }
17
+ //# sourceMappingURL=interaction-session-reader.js.map
@@ -0,0 +1,86 @@
1
+ import { SessionApiMapper } from "../api/session-api-mapper.js";
2
+ import { CatalogSnapshotStore, DEFAULT_CATALOG_FRESHNESS_MS, } from "../repository/catalog-snapshot-store.js";
3
+ import { SessionQueries } from "../repository/session-queries.js";
4
+ import { createUpdateRevision } from "../updates/update-revision.js";
5
+ import { CatalogInteractionSessionReader } from "./interaction-session-reader.js";
6
+ import { assembleSessionResponse } from "./session-response.js";
7
+ export class SessionReadService {
8
+ runtime;
9
+ #store;
10
+ #queries;
11
+ #mapper;
12
+ #interaction;
13
+ #interactionSessions;
14
+ constructor(sources, options = {}) {
15
+ const { store, interaction, resolveDisplayTitle, agents, freshnessMs = DEFAULT_CATALOG_FRESHNESS_MS, now = performance.now.bind(performance), runtime, ...storeDependencies } = options;
16
+ this.runtime = runtime;
17
+ this.#mapper = new SessionApiMapper(resolveDisplayTitle, agents);
18
+ this.#store = store ?? new CatalogSnapshotStore(sources, freshnessMs, now, storeDependencies);
19
+ this.#interaction = interaction;
20
+ this.#interactionSessions = new CatalogInteractionSessionReader(this.#store);
21
+ this.#queries = new SessionQueries();
22
+ }
23
+ async managementSnapshot() {
24
+ const snapshot = await this.#store.refresh();
25
+ const byId = new Map([...snapshot.sessions].map(([id, value]) => [id, value.session]));
26
+ return { sessions: [...byId.values()], byId };
27
+ }
28
+ async refresh() {
29
+ await this.#store.refresh();
30
+ }
31
+ async list() {
32
+ const snapshot = await this.#store.current();
33
+ const sessions = [...snapshot.sessions.values()].map(({ session }) => session);
34
+ if (!this.runtime)
35
+ return this.#mapper.list(sessions);
36
+ const states = await Promise.all(sessions.map((session) => this.runtime.evaluate(session, false)));
37
+ const byId = new Map(sessions.map((session, index) => [session.id, states[index].status]));
38
+ return this.#mapper.list(sessions, byId);
39
+ }
40
+ async getSession(id) {
41
+ return (await this.readMessages(id))?.state ?? null;
42
+ }
43
+ async messages(id, query) {
44
+ return (await this.readMessages(id))?.page(query) ?? null;
45
+ }
46
+ async readMessages(id) {
47
+ const snapshot = await this.#store.current();
48
+ const source = this.#queries.session(snapshot, id);
49
+ if (!source)
50
+ return null;
51
+ const session = this.#mapper.sessionDetail(source);
52
+ const interactionSession = await this.#runtimeDetail(session, snapshot);
53
+ const state = await assembleSessionResponse({ session, interactionSession }, this.#interaction, createUpdateRevision);
54
+ return {
55
+ state,
56
+ page: (query) => {
57
+ const result = this.#queries.messages(snapshot, id, query);
58
+ return {
59
+ ...state,
60
+ items: result.items,
61
+ nextAfter: result.nextAfter,
62
+ hasMore: result.hasMore,
63
+ };
64
+ },
65
+ };
66
+ }
67
+ async #runtimeDetail(detail, snapshot) {
68
+ const normalized = snapshot.sessions.get(detail.id);
69
+ const interactionSession = {
70
+ archived: normalized.session.archived,
71
+ interaction: normalized.interaction ?? null,
72
+ };
73
+ if (!this.runtime)
74
+ return interactionSession;
75
+ const observation = normalized.session.archived
76
+ ? undefined
77
+ : await this.runtime.observe(detail.id);
78
+ const { status, resume } = await this.runtime.evaluate(normalized.session, true, observation);
79
+ Object.assign(detail, { status, resume });
80
+ return { ...interactionSession, observation };
81
+ }
82
+ getInteractionSession(id) {
83
+ return this.#interactionSessions.getInteractionSession(id);
84
+ }
85
+ }
86
+ //# sourceMappingURL=session-read-service.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=session-reader.js.map
@@ -0,0 +1,11 @@
1
+ import { reconcileSessionState } from "../interaction/reconcile-session-state.js";
2
+ import { withUpdateRevision } from "../updates/update-revision.js";
3
+ /** Assemble public reader state from one source snapshot and its runtime observation. */
4
+ export async function assembleSessionResponse(input, interaction, revision) {
5
+ const { interactionSession, ...response } = input;
6
+ const description = interaction
7
+ ? await interaction.describeSnapshot(input.session.id, interactionSession)
8
+ : { supported: false, attachCommand: null };
9
+ return withUpdateRevision(reconcileSessionState({ ...response, interaction: description }), revision);
10
+ }
11
+ //# sourceMappingURL=session-response.js.map
@@ -0,0 +1,4 @@
1
+ export const CATALOG_PROBE_INTERVAL_MS = 1_500;
2
+ export const CATALOG_WAIT_TIMEOUT_MS = 25_000;
3
+ export const MAX_CATALOG_WAITERS = 100;
4
+ //# sourceMappingURL=config.js.map