@wrongstack/core 0.270.0 → 0.272.1
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/{agent-bridge-PcHQl_UQ.d.ts → agent-bridge-DFQYEeXf.d.ts} +1 -1
- package/dist/{agent-subagent-runner-SHJW7t8q.d.ts → agent-subagent-runner-BZa_IEcd.d.ts} +7 -7
- package/dist/{brain-BYcK__Ym.d.ts → brain-etbcbRwV.d.ts} +95 -2
- package/dist/{compactor-C2RKEBtC.d.ts → compactor-72ug-ZRB.d.ts} +1 -1
- package/dist/{config-C_ae2k86.d.ts → config-rRS8yorV.d.ts} +71 -2
- package/dist/{context-Dp87Bcaq.d.ts → context-Dw55zZ_Q.d.ts} +110 -1
- package/dist/coordination/index.d.ts +181 -17
- package/dist/coordination/index.js +1018 -166
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +804 -222
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +23 -18
- package/dist/execution/index.js +136 -41
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +36 -6
- package/dist/execution/prompt-enhancer.js +35 -9
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{global-mailbox-Bvrz1P3f.d.ts → global-mailbox-DJ4EoRr0.d.ts} +145 -5
- package/dist/{goal-preamble-CA_4yiGQ.d.ts → goal-preamble-hM8BH7TK.d.ts} +9 -9
- package/dist/{goal-store-DhuJoUNG.d.ts → goal-store-CWlbT0TO.d.ts} +1 -1
- package/dist/hq/index.d.ts +95 -6
- package/dist/hq/index.js +628 -50
- package/dist/hq/index.js.map +1 -1
- package/dist/{index-whDfTANu.d.ts → index-2Lhk5v0o.d.ts} +2 -2
- package/dist/{index-W4VJCzHa.d.ts → index-DWm_PE9L.d.ts} +5 -5
- package/dist/{index-CZQ6Pwbs.d.ts → index-DqW4o62H.d.ts} +8 -8
- package/dist/index.d.ts +96 -56
- package/dist/index.js +2464 -519
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +5 -3
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-DJdZiRcv.d.ts → mcp-servers-BpWHTKlE.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +28 -5
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-C3a-2-Yd.d.ts → models-registry-CXQFUn5t.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CJSpTe5O.d.ts → multi-agent-coordinator-jyimfo7D.d.ts} +1 -1
- package/dist/{null-fleet-bus-QVshIsDx.d.ts → null-fleet-bus-DOGQcvrY.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-D9y5Pkcc.d.ts → parallel-eternal-engine-rItJBYp9.d.ts} +9 -9
- package/dist/{path-resolver-CnQ8SIfh.d.ts → path-resolver-DrpF5MGK.d.ts} +3 -3
- package/dist/{permission-CvYQNUqZ.d.ts → permission-CC7XFYWG.d.ts} +1 -1
- package/dist/{permission-policy-D5Ss8j4B.d.ts → permission-policy-cYR4RJmw.d.ts} +2 -2
- package/dist/{pipeline-l_zzFRh3.d.ts → pipeline-Ckkn3AOA.d.ts} +2 -2
- package/dist/{plan-templates-NtPgyeJA.d.ts → plan-templates-BvHw5Znw.d.ts} +33 -9
- package/dist/{provider-model-resolve-d5poT5y0.d.ts → provider-model-resolve-nZqnCeaR.d.ts} +3 -3
- package/dist/{provider-runner-gkctlQV_.d.ts → provider-runner-zVOn1p67.d.ts} +3 -3
- package/dist/{retry-policy-CtFhfwa8.d.ts → retry-policy-BV7nzeAd.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +2 -0
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-BLsVmTIK.d.ts → secret-vault-eMBKfheR.d.ts} +9 -1
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +137 -10
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-CXl2_y9W.d.ts → selector-C4ORTOid.d.ts} +1 -1
- package/dist/{session-event-bridge-Ccud20CC.d.ts → session-event-bridge-CeNpUL9w.d.ts} +1 -1
- package/dist/{session-reader-ZeXQmsmE.d.ts → session-reader-BepLSnGL.d.ts} +1 -1
- package/dist/storage/index.d.ts +50 -13
- package/dist/storage/index.js +620 -220
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +9 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +202 -41
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +17 -4
- package/dist/utils/index.js +48 -9
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/hq/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
|
2
2
|
import * as syncFs from 'fs';
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
import { hostname } from 'os';
|
|
5
|
-
import * as
|
|
5
|
+
import * as path2 from 'path';
|
|
6
6
|
import { basename } from 'path';
|
|
7
7
|
import * as fsp from 'fs/promises';
|
|
8
8
|
|
|
@@ -105,7 +105,13 @@ function parseHqFrame(raw) {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
var KNOWN_HQ_EVENT_PAYLOAD_TYPES = /* @__PURE__ */ new Set([
|
|
108
|
+
var KNOWN_HQ_EVENT_PAYLOAD_TYPES = /* @__PURE__ */ new Set([
|
|
109
|
+
"mailbox.snapshot",
|
|
110
|
+
"mailbox.event",
|
|
111
|
+
"session.snapshot",
|
|
112
|
+
"session.transcript",
|
|
113
|
+
"session.ended"
|
|
114
|
+
]);
|
|
109
115
|
function isHqMailboxMessageSummary(x) {
|
|
110
116
|
if (typeof x !== "object" || x === null) return false;
|
|
111
117
|
const v = x;
|
|
@@ -152,6 +158,52 @@ function isHqMailboxEventPayload(x) {
|
|
|
152
158
|
if (v.agent !== void 0 && !isHqMailboxAgentSummary(v.agent)) return false;
|
|
153
159
|
return true;
|
|
154
160
|
}
|
|
161
|
+
var HQ_SESSION_AGENT_STATUSES = /* @__PURE__ */ new Set([
|
|
162
|
+
"idle",
|
|
163
|
+
"running",
|
|
164
|
+
"streaming",
|
|
165
|
+
"waiting_user",
|
|
166
|
+
"error"
|
|
167
|
+
]);
|
|
168
|
+
function isHqSessionAgentSummary(x) {
|
|
169
|
+
if (typeof x !== "object" || x === null) return false;
|
|
170
|
+
const v = x;
|
|
171
|
+
return typeof v.id === "string" && typeof v.name === "string" && typeof v.status === "string" && HQ_SESSION_AGENT_STATUSES.has(v.status) && typeof v.iterations === "number" && typeof v.toolCalls === "number" && typeof v.lastActivityAt === "string";
|
|
172
|
+
}
|
|
173
|
+
var HQ_SESSION_STATUSES = /* @__PURE__ */ new Set(["active", "idle", "closing", "stale"]);
|
|
174
|
+
function isHqSessionSnapshotPayload(x) {
|
|
175
|
+
if (typeof x !== "object" || x === null) return false;
|
|
176
|
+
const v = x;
|
|
177
|
+
if (typeof v.sessionId !== "string" || typeof v.clientKind !== "string" || typeof v.machineId !== "string" || typeof v.projectId !== "string" || typeof v.projectName !== "string" || typeof v.projectRoot !== "string" || typeof v.status !== "string" || !HQ_SESSION_STATUSES.has(v.status) || typeof v.startedAt !== "string" || typeof v.lastActivityAt !== "string" || typeof v.agentCount !== "number" || !Array.isArray(v.agents)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
for (const agent of v.agents) {
|
|
181
|
+
if (!isHqSessionAgentSummary(agent)) return false;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
var HQ_TRANSCRIPT_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool", "system", "error"]);
|
|
186
|
+
function isHqTranscriptEntry(x) {
|
|
187
|
+
if (typeof x !== "object" || x === null) return false;
|
|
188
|
+
const v = x;
|
|
189
|
+
return typeof v.ts === "string" && typeof v.role === "string" && HQ_TRANSCRIPT_ROLES.has(v.role) && typeof v.text === "string";
|
|
190
|
+
}
|
|
191
|
+
function isHqTranscriptAppendPayload(x) {
|
|
192
|
+
if (typeof x !== "object" || x === null) return false;
|
|
193
|
+
const v = x;
|
|
194
|
+
if (typeof v.sessionId !== "string" || typeof v.fromSeq !== "number" || !Array.isArray(v.entries)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
for (const entry of v.entries) {
|
|
198
|
+
if (!isHqTranscriptEntry(entry)) return false;
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
function isHqSessionEndedPayload(x) {
|
|
203
|
+
if (typeof x !== "object" || x === null) return false;
|
|
204
|
+
const v = x;
|
|
205
|
+
return typeof v.sessionId === "string" && typeof v.endedAt === "string";
|
|
206
|
+
}
|
|
155
207
|
function parseHqEventPayload(eventType, payload) {
|
|
156
208
|
if (!KNOWN_HQ_EVENT_PAYLOAD_TYPES.has(eventType)) {
|
|
157
209
|
return { ok: true, payload };
|
|
@@ -161,6 +213,12 @@ function parseHqEventPayload(eventType, payload) {
|
|
|
161
213
|
return isHqMailboxSnapshotPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
162
214
|
case "mailbox.event":
|
|
163
215
|
return isHqMailboxEventPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
216
|
+
case "session.snapshot":
|
|
217
|
+
return isHqSessionSnapshotPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
218
|
+
case "session.transcript":
|
|
219
|
+
return isHqTranscriptAppendPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
220
|
+
case "session.ended":
|
|
221
|
+
return isHqSessionEndedPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
164
222
|
default: {
|
|
165
223
|
const _exhaustive = eventType;
|
|
166
224
|
return _exhaustive;
|
|
@@ -797,6 +855,41 @@ var HqPublisher = class {
|
|
|
797
855
|
...input.timestamp !== void 0 ? { timestamp: input.timestamp } : {}
|
|
798
856
|
});
|
|
799
857
|
}
|
|
858
|
+
/** The client identity this publisher announced (clientId, kind, machineId, …). */
|
|
859
|
+
get identity() {
|
|
860
|
+
return this.options.client;
|
|
861
|
+
}
|
|
862
|
+
/** The project identity this publisher is bound to. */
|
|
863
|
+
get project() {
|
|
864
|
+
return this.options.project;
|
|
865
|
+
}
|
|
866
|
+
/** Publish a live session/terminal snapshot (state + agents). */
|
|
867
|
+
publishSessionSnapshot(payload, opts) {
|
|
868
|
+
return this.publishEvent({
|
|
869
|
+
type: "session.snapshot",
|
|
870
|
+
payload,
|
|
871
|
+
sessionId: payload.sessionId,
|
|
872
|
+
...opts?.timestamp !== void 0 ? { timestamp: opts.timestamp } : {}
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
/** Publish an incremental batch of transcript turns for a session. */
|
|
876
|
+
publishTranscriptAppend(payload, opts) {
|
|
877
|
+
return this.publishEvent({
|
|
878
|
+
type: "session.transcript",
|
|
879
|
+
payload,
|
|
880
|
+
sessionId: payload.sessionId,
|
|
881
|
+
...opts?.timestamp !== void 0 ? { timestamp: opts.timestamp } : {}
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
/** Mark a session/terminal as ended. */
|
|
885
|
+
publishSessionEnded(payload, opts) {
|
|
886
|
+
return this.publishEvent({
|
|
887
|
+
type: "session.ended",
|
|
888
|
+
payload,
|
|
889
|
+
sessionId: payload.sessionId,
|
|
890
|
+
...opts?.timestamp !== void 0 ? { timestamp: opts.timestamp } : {}
|
|
891
|
+
});
|
|
892
|
+
}
|
|
800
893
|
pollCommands() {
|
|
801
894
|
this.sendFrame({
|
|
802
895
|
type: "client.command_poll",
|
|
@@ -914,9 +1007,9 @@ var HqPublisher = class {
|
|
|
914
1007
|
}
|
|
915
1008
|
};
|
|
916
1009
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
917
|
-
const dir =
|
|
1010
|
+
const dir = path2.dirname(targetPath);
|
|
918
1011
|
await fsp.mkdir(dir, { recursive: true });
|
|
919
|
-
const tmp =
|
|
1012
|
+
const tmp = path2.join(dir, `.${path2.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
920
1013
|
try {
|
|
921
1014
|
if (typeof content === "string") {
|
|
922
1015
|
await fsp.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
@@ -934,8 +1027,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
934
1027
|
}
|
|
935
1028
|
let mode;
|
|
936
1029
|
try {
|
|
937
|
-
const
|
|
938
|
-
mode =
|
|
1030
|
+
const stat4 = await fsp.stat(targetPath);
|
|
1031
|
+
mode = stat4.mode & 511;
|
|
939
1032
|
} catch {
|
|
940
1033
|
mode = opts.mode;
|
|
941
1034
|
}
|
|
@@ -952,9 +1045,9 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
952
1045
|
}
|
|
953
1046
|
}
|
|
954
1047
|
async function withFileLock(targetPath, fn, opts = {}) {
|
|
955
|
-
const dir =
|
|
1048
|
+
const dir = path2.dirname(targetPath);
|
|
956
1049
|
await fsp.mkdir(dir, { recursive: true });
|
|
957
|
-
const lockPath =
|
|
1050
|
+
const lockPath = path2.join(dir, `.${path2.basename(targetPath)}.lock`);
|
|
958
1051
|
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
959
1052
|
const staleMs = opts.staleMs ?? 3e4;
|
|
960
1053
|
const started = Date.now();
|
|
@@ -972,8 +1065,8 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
972
1065
|
}
|
|
973
1066
|
if (code !== "EEXIST") throw err;
|
|
974
1067
|
try {
|
|
975
|
-
const
|
|
976
|
-
if (Date.now() -
|
|
1068
|
+
const stat4 = await fsp.stat(lockPath);
|
|
1069
|
+
if (Date.now() - stat4.mtimeMs > staleMs) {
|
|
977
1070
|
await fsp.unlink(lockPath);
|
|
978
1071
|
continue;
|
|
979
1072
|
}
|
|
@@ -1022,10 +1115,62 @@ async function renameWithRetry(from, to) {
|
|
|
1022
1115
|
}
|
|
1023
1116
|
throw lastErr;
|
|
1024
1117
|
}
|
|
1118
|
+
function projectHash(absRoot) {
|
|
1119
|
+
return createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
1120
|
+
}
|
|
1121
|
+
function projectSlug(absRoot) {
|
|
1122
|
+
const base = slugify(path2.basename(absRoot));
|
|
1123
|
+
const hash = createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
1124
|
+
return `${base}-${hash}`;
|
|
1125
|
+
}
|
|
1126
|
+
function slugify(name) {
|
|
1127
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
1128
|
+
}
|
|
1025
1129
|
function wstackGlobalRoot() {
|
|
1026
1130
|
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
1027
|
-
if (fromEnv && fromEnv.trim().length > 0) return
|
|
1028
|
-
return
|
|
1131
|
+
if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
|
|
1132
|
+
return path2.join(os.homedir(), ".wrongstack");
|
|
1133
|
+
}
|
|
1134
|
+
function resolveWstackPaths(opts) {
|
|
1135
|
+
const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
1136
|
+
const hash = projectHash(opts.projectRoot);
|
|
1137
|
+
const slug = projectSlug(opts.projectRoot);
|
|
1138
|
+
const projectDir = path2.join(globalRoot, "projects", slug);
|
|
1139
|
+
return {
|
|
1140
|
+
globalRoot,
|
|
1141
|
+
configDir: globalRoot,
|
|
1142
|
+
globalConfig: path2.join(globalRoot, "config.json"),
|
|
1143
|
+
secretsKey: path2.join(globalRoot, ".key"),
|
|
1144
|
+
globalMemory: path2.join(globalRoot, "memory.md"),
|
|
1145
|
+
globalSkills: path2.join(globalRoot, "skills"),
|
|
1146
|
+
globalPrompts: path2.join(globalRoot, "prompts"),
|
|
1147
|
+
cacheDir: path2.join(globalRoot, "cache"),
|
|
1148
|
+
modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
|
|
1149
|
+
modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
|
|
1150
|
+
historyFile: path2.join(globalRoot, "history"),
|
|
1151
|
+
logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
|
|
1152
|
+
projectDir,
|
|
1153
|
+
projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
|
|
1154
|
+
projectMemory: path2.join(projectDir, "memory.md"),
|
|
1155
|
+
projectSessions: path2.join(projectDir, "sessions"),
|
|
1156
|
+
projectTrust: path2.join(projectDir, "trust.json"),
|
|
1157
|
+
projectMeta: path2.join(projectDir, "meta.json"),
|
|
1158
|
+
projectLocalConfig: path2.join(projectDir, "config.local.json"),
|
|
1159
|
+
inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
1160
|
+
inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
1161
|
+
inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
1162
|
+
inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
1163
|
+
projectHash: hash,
|
|
1164
|
+
projectSlug: slug,
|
|
1165
|
+
projectGoal: path2.join(projectDir, "goal.json"),
|
|
1166
|
+
projectSpecs: path2.join(projectDir, "specs"),
|
|
1167
|
+
projectTaskGraphs: path2.join(projectDir, "task-graphs"),
|
|
1168
|
+
projectSddSession: path2.join(projectDir, "sdd-session.json"),
|
|
1169
|
+
projectPlan: path2.join(projectDir, "plan.json"),
|
|
1170
|
+
projectAutophase: path2.join(projectDir, "autophase"),
|
|
1171
|
+
syncConfig: path2.join(globalRoot, "sync.json"),
|
|
1172
|
+
projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
|
|
1173
|
+
};
|
|
1029
1174
|
}
|
|
1030
1175
|
|
|
1031
1176
|
// src/coordination/mailbox-types.ts
|
|
@@ -1090,27 +1235,31 @@ var GlobalMailbox = class {
|
|
|
1090
1235
|
/**
|
|
1091
1236
|
* @param projectDir — `~/.wrongstack/projects/<slug>/`
|
|
1092
1237
|
* @param events — optional EventBus for real-time TUI/WebUI notifications
|
|
1093
|
-
* @param hqPublisher — optional HQ publisher for cross-project telemetry
|
|
1238
|
+
* @param hqPublisher — optional HQ publisher, or getter, for cross-project telemetry
|
|
1094
1239
|
*/
|
|
1095
1240
|
constructor(projectDir, events, hqPublisher) {
|
|
1096
|
-
this.messagePath =
|
|
1097
|
-
this.registryPath =
|
|
1098
|
-
this.clientRegistryPath =
|
|
1241
|
+
this.messagePath = path2.join(projectDir, MAILBOX_FILE);
|
|
1242
|
+
this.registryPath = path2.join(projectDir, "_mailbox.registry.json");
|
|
1243
|
+
this.clientRegistryPath = path2.join(projectDir, CLIENT_REGISTRY_FILE);
|
|
1099
1244
|
this._events = events;
|
|
1100
1245
|
this._hqPublisher = hqPublisher;
|
|
1101
1246
|
}
|
|
1102
1247
|
get hqMailboxId() {
|
|
1103
|
-
return `${
|
|
1248
|
+
return `${path2.basename(path2.dirname(this.messagePath))}:mailbox`;
|
|
1249
|
+
}
|
|
1250
|
+
get hqPublisher() {
|
|
1251
|
+
return typeof this._hqPublisher === "function" ? this._hqPublisher() : this._hqPublisher;
|
|
1104
1252
|
}
|
|
1105
1253
|
publishHqMailboxEvent(input) {
|
|
1106
1254
|
try {
|
|
1107
|
-
this.
|
|
1255
|
+
this.hqPublisher?.publishMailboxEvent(input);
|
|
1108
1256
|
} catch {
|
|
1109
1257
|
}
|
|
1110
1258
|
}
|
|
1111
1259
|
publishHqMailboxSnapshot() {
|
|
1112
|
-
|
|
1113
|
-
|
|
1260
|
+
const publisher = this.hqPublisher;
|
|
1261
|
+
if (publisher === void 0) return;
|
|
1262
|
+
void publisher.publishMailboxSnapshot(this, { mailboxId: this.hqMailboxId }).catch(() => {
|
|
1114
1263
|
});
|
|
1115
1264
|
}
|
|
1116
1265
|
// ── Messages ────────────────────────────────────────────────────────────
|
|
@@ -1133,7 +1282,7 @@ var GlobalMailbox = class {
|
|
|
1133
1282
|
taskContext: input.taskContext
|
|
1134
1283
|
};
|
|
1135
1284
|
const line = JSON.stringify(msg) + LINE_SEPARATOR;
|
|
1136
|
-
await fsp.mkdir(
|
|
1285
|
+
await fsp.mkdir(path2.dirname(this.messagePath), { recursive: true });
|
|
1137
1286
|
await withFileLock(this.messagePath, async () => {
|
|
1138
1287
|
await fsp.appendFile(this.messagePath, line, "utf8");
|
|
1139
1288
|
this._pushToCache(msg);
|
|
@@ -1472,30 +1621,69 @@ var GlobalMailbox = class {
|
|
|
1472
1621
|
async _readMessages() {
|
|
1473
1622
|
try {
|
|
1474
1623
|
const raw = await fsp.readFile(this.messagePath, "utf8");
|
|
1475
|
-
|
|
1476
|
-
const messages = [];
|
|
1477
|
-
for (const line of lines) {
|
|
1478
|
-
try {
|
|
1479
|
-
const parsed = JSON.parse(line);
|
|
1480
|
-
if (!parsed["readBy"]) {
|
|
1481
|
-
const readBy = {};
|
|
1482
|
-
if (parsed["read"] && parsed["readAt"]) {
|
|
1483
|
-
readBy[parsed["to"]] = parsed["readAt"];
|
|
1484
|
-
}
|
|
1485
|
-
parsed["readBy"] = readBy;
|
|
1486
|
-
delete parsed["read"];
|
|
1487
|
-
delete parsed["readAt"];
|
|
1488
|
-
}
|
|
1489
|
-
messages.push(parsed);
|
|
1490
|
-
} catch {
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
return messages;
|
|
1624
|
+
return this._parseLines(raw);
|
|
1494
1625
|
} catch (err) {
|
|
1495
1626
|
if (err.code === "ENOENT") return [];
|
|
1496
1627
|
throw err;
|
|
1497
1628
|
}
|
|
1498
1629
|
}
|
|
1630
|
+
/**
|
|
1631
|
+
* Read only newly-appended bytes from the file and append them to the
|
|
1632
|
+
* in-memory cache. This avoids re-reading and re-parsing the entire file
|
|
1633
|
+
* when another process appended messages since our last read.
|
|
1634
|
+
*
|
|
1635
|
+
* Only safe when the file grew in size (messages are append-only). When
|
|
1636
|
+
* the file was rewritten (ack/purge changed existing content), callers
|
|
1637
|
+
* must fall back to {@link _readMessages}.
|
|
1638
|
+
*
|
|
1639
|
+
* @returns The (now up-to-date) message cache.
|
|
1640
|
+
*/
|
|
1641
|
+
async _readNewMessagesOnly(fd, oldSize, newSize) {
|
|
1642
|
+
const tailLen = newSize - oldSize;
|
|
1643
|
+
const buf = Buffer.alloc(tailLen);
|
|
1644
|
+
await fd.read(buf, 0, tailLen, oldSize);
|
|
1645
|
+
const tail = buf.toString("utf8");
|
|
1646
|
+
for (const line of tail.split(LINE_SEPARATOR)) {
|
|
1647
|
+
if (!line.trim()) continue;
|
|
1648
|
+
try {
|
|
1649
|
+
const parsed = JSON.parse(line);
|
|
1650
|
+
if (!parsed["readBy"]) {
|
|
1651
|
+
const readBy = {};
|
|
1652
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
1653
|
+
readBy[parsed["to"]] = parsed["readAt"];
|
|
1654
|
+
}
|
|
1655
|
+
parsed["readBy"] = readBy;
|
|
1656
|
+
delete parsed["read"];
|
|
1657
|
+
delete parsed["readAt"];
|
|
1658
|
+
}
|
|
1659
|
+
this._messageCache.push(parsed);
|
|
1660
|
+
} catch {
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
return this._messageCache;
|
|
1664
|
+
}
|
|
1665
|
+
/** Parse a JSONL string into MailboxMessage[], including migration. */
|
|
1666
|
+
_parseLines(raw) {
|
|
1667
|
+
const lines = raw.split(LINE_SEPARATOR).filter((l) => l.trim().length > 0);
|
|
1668
|
+
const messages = [];
|
|
1669
|
+
for (const line of lines) {
|
|
1670
|
+
try {
|
|
1671
|
+
const parsed = JSON.parse(line);
|
|
1672
|
+
if (!parsed["readBy"]) {
|
|
1673
|
+
const readBy = {};
|
|
1674
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
1675
|
+
readBy[parsed["to"]] = parsed["readAt"];
|
|
1676
|
+
}
|
|
1677
|
+
parsed["readBy"] = readBy;
|
|
1678
|
+
delete parsed["read"];
|
|
1679
|
+
delete parsed["readAt"];
|
|
1680
|
+
}
|
|
1681
|
+
messages.push(parsed);
|
|
1682
|
+
} catch {
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return messages;
|
|
1686
|
+
}
|
|
1499
1687
|
/**
|
|
1500
1688
|
* Read messages, then adopt the result as the in-memory cache. Use this
|
|
1501
1689
|
* from writers that just took the file lock — the read reflects the
|
|
@@ -1515,6 +1703,10 @@ var GlobalMailbox = class {
|
|
|
1515
1703
|
* stat matches the cached mtime+size we return the cached array — no
|
|
1516
1704
|
* file read and no JSON.parse — collapsing the per-iteration query
|
|
1517
1705
|
* cost on the mailbox-loop hot path.
|
|
1706
|
+
*
|
|
1707
|
+
* When the file only grew (new messages appended by another process),
|
|
1708
|
+
* we read and parse just the tail bytes instead of the entire file.
|
|
1709
|
+
* This avoids re-parsing the full 10K-message history on every check.
|
|
1518
1710
|
*/
|
|
1519
1711
|
async _readMessagesCached() {
|
|
1520
1712
|
try {
|
|
@@ -1522,6 +1714,17 @@ var GlobalMailbox = class {
|
|
|
1522
1714
|
if (this._messageCache !== null && this._messageCacheMtime === st.mtimeMs && this._messageCacheSize === st.size) {
|
|
1523
1715
|
return this._messageCache;
|
|
1524
1716
|
}
|
|
1717
|
+
if (this._messageCache !== null && this._messageCacheSize >= 0 && st.size > this._messageCacheSize) {
|
|
1718
|
+
const fd = await fsp.open(this.messagePath, "r");
|
|
1719
|
+
try {
|
|
1720
|
+
const updated = await this._readNewMessagesOnly(fd, this._messageCacheSize, st.size);
|
|
1721
|
+
this._messageCacheMtime = st.mtimeMs;
|
|
1722
|
+
this._messageCacheSize = st.size;
|
|
1723
|
+
return updated;
|
|
1724
|
+
} finally {
|
|
1725
|
+
await fd.close();
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1525
1728
|
const all = await this._readMessages();
|
|
1526
1729
|
this._setMessageCache(all, st.mtimeMs, st.size);
|
|
1527
1730
|
return all;
|
|
@@ -1574,7 +1777,7 @@ var GlobalMailbox = class {
|
|
|
1574
1777
|
this._messageCache.push(msg);
|
|
1575
1778
|
}
|
|
1576
1779
|
async _ensureRegistry() {
|
|
1577
|
-
await fsp.mkdir(
|
|
1780
|
+
await fsp.mkdir(path2.dirname(this.registryPath), { recursive: true });
|
|
1578
1781
|
}
|
|
1579
1782
|
async _readRegistry(opts) {
|
|
1580
1783
|
if (!opts?.fresh && this._registryCache && Date.now() - this._registryCacheAt < REGISTRY_CACHE_TTL_MS) {
|
|
@@ -1619,7 +1822,7 @@ var GlobalMailbox = class {
|
|
|
1619
1822
|
}
|
|
1620
1823
|
// ── Client registry internals ───────────────────────────────────────────
|
|
1621
1824
|
async _ensureClientRegistry() {
|
|
1622
|
-
await fsp.mkdir(
|
|
1825
|
+
await fsp.mkdir(path2.dirname(this.clientRegistryPath), { recursive: true });
|
|
1623
1826
|
}
|
|
1624
1827
|
async _readClientRegistry(opts) {
|
|
1625
1828
|
if (!opts?.fresh && this._clientRegistryCache && Date.now() - this._clientRegistryCacheAt < REGISTRY_CACHE_TTL_MS) {
|
|
@@ -1665,12 +1868,12 @@ var GlobalMailbox = class {
|
|
|
1665
1868
|
};
|
|
1666
1869
|
var HQ_AUTH_FILE_VERSION = 1;
|
|
1667
1870
|
function defaultHqDataDir() {
|
|
1668
|
-
return
|
|
1871
|
+
return path2.join(wstackGlobalRoot(), "hq");
|
|
1669
1872
|
}
|
|
1670
1873
|
function resolveHqDataDir(override, env = process.env) {
|
|
1671
1874
|
const raw = override ?? env["WRONGSTACK_HQ_DATA_DIR"]?.trim();
|
|
1672
1875
|
if (!raw) return defaultHqDataDir();
|
|
1673
|
-
return
|
|
1876
|
+
return path2.isAbsolute(raw) ? path2.resolve(raw) : path2.resolve(process.cwd(), raw);
|
|
1674
1877
|
}
|
|
1675
1878
|
function emptyHqAuthFile() {
|
|
1676
1879
|
return {
|
|
@@ -1679,10 +1882,10 @@ function emptyHqAuthFile() {
|
|
|
1679
1882
|
};
|
|
1680
1883
|
}
|
|
1681
1884
|
function hqAuthFilePath(dataDir) {
|
|
1682
|
-
return
|
|
1885
|
+
return path2.join(dataDir, "auth.json");
|
|
1683
1886
|
}
|
|
1684
1887
|
function hqRuntimeFilePath(dataDir) {
|
|
1685
|
-
return
|
|
1888
|
+
return path2.join(dataDir, "runtime.json");
|
|
1686
1889
|
}
|
|
1687
1890
|
async function writeHqRuntimeFile(dataDir, file) {
|
|
1688
1891
|
const payload = {
|
|
@@ -1794,7 +1997,7 @@ function watchHqAuthFile(dataDir, onChange, opts = {}) {
|
|
|
1794
1997
|
let closed = false;
|
|
1795
1998
|
let watcher;
|
|
1796
1999
|
try {
|
|
1797
|
-
watcher = syncFs.watch(
|
|
2000
|
+
watcher = syncFs.watch(path2.dirname(file), { recursive: false });
|
|
1798
2001
|
} catch (err) {
|
|
1799
2002
|
opts.warn?.(`HQ auth watcher could not start: ${err.message}`);
|
|
1800
2003
|
return { close: () => {
|
|
@@ -1818,7 +2021,7 @@ function watchHqAuthFile(dataDir, onChange, opts = {}) {
|
|
|
1818
2021
|
watcher.on("change", (eventType, filename) => {
|
|
1819
2022
|
const name = typeof filename === "string" ? filename : "";
|
|
1820
2023
|
if (eventType === "rename" || eventType === "change") {
|
|
1821
|
-
if (!name || name === "auth.json" || name ===
|
|
2024
|
+
if (!name || name === "auth.json" || name === path2.basename(file)) {
|
|
1822
2025
|
trigger();
|
|
1823
2026
|
}
|
|
1824
2027
|
}
|
|
@@ -1883,7 +2086,7 @@ function resolveHqConfig(options = {}) {
|
|
|
1883
2086
|
};
|
|
1884
2087
|
}
|
|
1885
2088
|
function stableMachineId() {
|
|
1886
|
-
return createHash("sha256").update(
|
|
2089
|
+
return createHash("sha256").update(hostname()).digest("hex").slice(0, 12);
|
|
1887
2090
|
}
|
|
1888
2091
|
function deriveProjectId(projectRoot) {
|
|
1889
2092
|
return createHash("sha256").update(projectRoot).digest("hex").slice(0, 12);
|
|
@@ -1926,6 +2129,381 @@ function createGlobalMailbox(options) {
|
|
|
1926
2129
|
return new GlobalMailbox(options.projectDir, options.events, options.hqPublisher);
|
|
1927
2130
|
}
|
|
1928
2131
|
|
|
1929
|
-
|
|
2132
|
+
// src/hq/agent-bridge.ts
|
|
2133
|
+
function startAgentMonitorEventBridge(opts) {
|
|
2134
|
+
const { events, clientId, projectId, publish } = opts;
|
|
2135
|
+
const seq = { current: 0 };
|
|
2136
|
+
function nextSeq() {
|
|
2137
|
+
seq.current += 1;
|
|
2138
|
+
return seq.current;
|
|
2139
|
+
}
|
|
2140
|
+
function buildEnvelope(type, payload) {
|
|
2141
|
+
return createHqEventEnvelope({
|
|
2142
|
+
id: `${Date.now().toString(36)}-${nextSeq()}`,
|
|
2143
|
+
type,
|
|
2144
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2145
|
+
clientId,
|
|
2146
|
+
projectId,
|
|
2147
|
+
seq: nextSeq(),
|
|
2148
|
+
payload
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
const offMessage = events.on("agent.timeline.message", (payload) => {
|
|
2152
|
+
if (!publish) return;
|
|
2153
|
+
const msgPayload = {
|
|
2154
|
+
subagentId: payload.subagentId,
|
|
2155
|
+
agentName: payload.agentName,
|
|
2156
|
+
content: payload.content,
|
|
2157
|
+
kind: payload.kind,
|
|
2158
|
+
iteration: payload.iteration,
|
|
2159
|
+
ts: payload.ts
|
|
2160
|
+
};
|
|
2161
|
+
if (payload.toolName !== void 0) msgPayload.toolName = payload.toolName;
|
|
2162
|
+
if (payload.costUsd !== void 0) msgPayload.costUsd = payload.costUsd;
|
|
2163
|
+
publish(buildEnvelope("agent.message", msgPayload));
|
|
2164
|
+
});
|
|
2165
|
+
const offStatus = events.on("agent.status_changed", (payload) => {
|
|
2166
|
+
if (!publish) return;
|
|
2167
|
+
const statusPayload = {
|
|
2168
|
+
subagentId: payload.subagentId,
|
|
2169
|
+
agentName: payload.agentName,
|
|
2170
|
+
status: payload.status,
|
|
2171
|
+
ts: payload.ts
|
|
2172
|
+
};
|
|
2173
|
+
if (payload.summary !== void 0) statusPayload.summary = payload.summary;
|
|
2174
|
+
if (payload.task !== void 0) statusPayload.task = payload.task;
|
|
2175
|
+
publish(buildEnvelope("agent.status", statusPayload));
|
|
2176
|
+
});
|
|
2177
|
+
return () => {
|
|
2178
|
+
offMessage();
|
|
2179
|
+
offStatus();
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
// src/hq/transcript-mapper.ts
|
|
2184
|
+
function blocksToText(content) {
|
|
2185
|
+
if (typeof content === "string") return content;
|
|
2186
|
+
if (Array.isArray(content)) {
|
|
2187
|
+
return content.filter(
|
|
2188
|
+
(b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string"
|
|
2189
|
+
).map((b) => b.text).join("\n");
|
|
2190
|
+
}
|
|
2191
|
+
return "";
|
|
2192
|
+
}
|
|
2193
|
+
function asString(v) {
|
|
2194
|
+
if (typeof v === "string") return v;
|
|
2195
|
+
try {
|
|
2196
|
+
return JSON.stringify(v, null, 2);
|
|
2197
|
+
} catch {
|
|
2198
|
+
return String(v);
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
function argsEntry(ts, name, input, id) {
|
|
2202
|
+
return {
|
|
2203
|
+
ts,
|
|
2204
|
+
role: "tool",
|
|
2205
|
+
tool: String(name ?? "tool"),
|
|
2206
|
+
toolInput: input !== void 0 && input !== null ? asString(input) : "{}",
|
|
2207
|
+
text: "",
|
|
2208
|
+
...typeof id === "string" ? { toolUseId: id } : {}
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
function mapSessionEventToEntries(ev) {
|
|
2212
|
+
const ts = typeof ev["ts"] === "string" ? ev["ts"] : "";
|
|
2213
|
+
const make = (role, text, extra) => ({
|
|
2214
|
+
ts,
|
|
2215
|
+
role,
|
|
2216
|
+
text,
|
|
2217
|
+
...extra
|
|
2218
|
+
});
|
|
2219
|
+
switch (ev["type"]) {
|
|
2220
|
+
case "user_input": {
|
|
2221
|
+
const t = blocksToText(ev["content"]);
|
|
2222
|
+
return t.trim() ? [make("user", t)] : [];
|
|
2223
|
+
}
|
|
2224
|
+
case "llm_response": {
|
|
2225
|
+
const out = [];
|
|
2226
|
+
const t = blocksToText(ev["content"]);
|
|
2227
|
+
if (t.trim()) out.push(make("assistant", t));
|
|
2228
|
+
const content = ev["content"];
|
|
2229
|
+
if (Array.isArray(content)) {
|
|
2230
|
+
for (const b of content) {
|
|
2231
|
+
if (b && typeof b === "object" && b.type === "tool_use") {
|
|
2232
|
+
const block = b;
|
|
2233
|
+
out.push(argsEntry(ts, block.name, block.input, block.id));
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
return out;
|
|
2238
|
+
}
|
|
2239
|
+
case "tool_use":
|
|
2240
|
+
return [argsEntry(ts, ev["name"], ev["input"], ev["id"])];
|
|
2241
|
+
case "tool_call_start":
|
|
2242
|
+
return [argsEntry(ts, ev["name"], ev["input"] ?? ev["args"], ev["id"])];
|
|
2243
|
+
case "tool_result": {
|
|
2244
|
+
const isError = ev["isError"] === true;
|
|
2245
|
+
const content = ev["content"] ?? ev["output"];
|
|
2246
|
+
const outStr = content !== void 0 && content !== null ? asString(content) : "";
|
|
2247
|
+
return [
|
|
2248
|
+
make(isError ? "error" : "tool", outStr, {
|
|
2249
|
+
tool: "\u21B3 result",
|
|
2250
|
+
...isError ? { isError: true } : {},
|
|
2251
|
+
...typeof ev["id"] === "string" ? { toolUseId: ev["id"] } : {}
|
|
2252
|
+
})
|
|
2253
|
+
];
|
|
2254
|
+
}
|
|
2255
|
+
case "tool_call_end": {
|
|
2256
|
+
const isError = ev["isError"] === true;
|
|
2257
|
+
const content = ev["output"] ?? ev["content"];
|
|
2258
|
+
const outStr = content !== void 0 && content !== null ? asString(content) : "";
|
|
2259
|
+
if (!outStr.trim() && !isError && typeof ev["durationMs"] !== "number") return [];
|
|
2260
|
+
return [
|
|
2261
|
+
make(isError ? "error" : "tool", outStr, {
|
|
2262
|
+
tool: typeof ev["name"] === "string" ? String(ev["name"]) : "\u21B3 result",
|
|
2263
|
+
...typeof ev["durationMs"] === "number" ? { durationMs: ev["durationMs"] } : {},
|
|
2264
|
+
...isError ? { isError: true } : {},
|
|
2265
|
+
...typeof ev["id"] === "string" ? { toolUseId: ev["id"] } : {}
|
|
2266
|
+
})
|
|
2267
|
+
];
|
|
2268
|
+
}
|
|
2269
|
+
case "error":
|
|
2270
|
+
case "provider_error":
|
|
2271
|
+
return [make("error", String(ev["message"] ?? "error"))];
|
|
2272
|
+
case "agent_spawned":
|
|
2273
|
+
return [make("system", `spawned ${String(ev["role"] ?? "agent")}`)];
|
|
2274
|
+
case "task_completed":
|
|
2275
|
+
return [make("system", `task done: ${String(ev["title"] ?? "")}`)];
|
|
2276
|
+
case "task_failed":
|
|
2277
|
+
return [make("system", `task failed: ${String(ev["title"] ?? "")}`)];
|
|
2278
|
+
default:
|
|
2279
|
+
return [];
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
function isResultEntry(e) {
|
|
2283
|
+
return (e.role === "tool" || e.role === "error") && e.toolUseId !== void 0 && e.toolInput === void 0;
|
|
2284
|
+
}
|
|
2285
|
+
function mergeToolResults(flat) {
|
|
2286
|
+
const out = [];
|
|
2287
|
+
const argsById = /* @__PURE__ */ new Map();
|
|
2288
|
+
for (const src of flat) {
|
|
2289
|
+
const e = { ...src };
|
|
2290
|
+
if (isResultEntry(e) && e.toolUseId !== void 0) {
|
|
2291
|
+
const tgt = argsById.get(e.toolUseId);
|
|
2292
|
+
if (tgt) {
|
|
2293
|
+
tgt.text = e.text || "";
|
|
2294
|
+
if (e.durationMs !== void 0) tgt.durationMs = e.durationMs;
|
|
2295
|
+
if (e.isError) {
|
|
2296
|
+
tgt.role = "error";
|
|
2297
|
+
tgt.isError = true;
|
|
2298
|
+
}
|
|
2299
|
+
continue;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
out.push(e);
|
|
2303
|
+
if (e.role === "tool" && e.toolUseId !== void 0 && e.toolInput !== void 0) {
|
|
2304
|
+
argsById.set(e.toolUseId, e);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
return out;
|
|
2308
|
+
}
|
|
2309
|
+
function buildTranscriptFromEvents(events) {
|
|
2310
|
+
const flat = [];
|
|
2311
|
+
for (const ev of events) {
|
|
2312
|
+
for (const e of mapSessionEventToEntries(ev)) flat.push(e);
|
|
2313
|
+
}
|
|
2314
|
+
return mergeToolResults(flat);
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// src/hq/session-bridge.ts
|
|
2318
|
+
var VALID_AGENT_STATUS = /* @__PURE__ */ new Set([
|
|
2319
|
+
"idle",
|
|
2320
|
+
"running",
|
|
2321
|
+
"streaming",
|
|
2322
|
+
"waiting_user",
|
|
2323
|
+
"error"
|
|
2324
|
+
]);
|
|
2325
|
+
function toAgentSummary(a) {
|
|
2326
|
+
const status = VALID_AGENT_STATUS.has(a.status) ? a.status : "idle";
|
|
2327
|
+
return {
|
|
2328
|
+
id: a.id,
|
|
2329
|
+
name: a.name,
|
|
2330
|
+
status,
|
|
2331
|
+
iterations: a.iterations,
|
|
2332
|
+
toolCalls: a.toolCalls,
|
|
2333
|
+
lastActivityAt: a.lastActivityAt,
|
|
2334
|
+
...a.startedAt !== void 0 ? { startedAt: a.startedAt } : {},
|
|
2335
|
+
...a.currentTool !== void 0 ? { currentTool: a.currentTool } : {},
|
|
2336
|
+
...a.costUsd !== void 0 ? { costUsd: a.costUsd } : {},
|
|
2337
|
+
...a.tokensIn !== void 0 ? { tokensIn: a.tokensIn } : {},
|
|
2338
|
+
...a.tokensOut !== void 0 ? { tokensOut: a.tokensOut } : {},
|
|
2339
|
+
...a.ctxPct !== void 0 ? { ctxPct: a.ctxPct } : {},
|
|
2340
|
+
...a.model !== void 0 ? { model: a.model } : {},
|
|
2341
|
+
...a.partialText !== void 0 ? { partialText: a.partialText } : {}
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
function deriveSessionStatus(agents) {
|
|
2345
|
+
return agents.some(
|
|
2346
|
+
(a) => a.status === "running" || a.status === "streaming" || a.status === "waiting_user"
|
|
2347
|
+
) ? "active" : "idle";
|
|
2348
|
+
}
|
|
2349
|
+
function startSessionTelemetryBridge(opts) {
|
|
2350
|
+
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
2351
|
+
const publisher = opts.publisher;
|
|
2352
|
+
const identity = publisher.identity;
|
|
2353
|
+
const project = publisher.project;
|
|
2354
|
+
const startedAt = opts.startedAt ?? now();
|
|
2355
|
+
const wpaths = resolveWstackPaths({
|
|
2356
|
+
projectRoot: opts.projectRoot,
|
|
2357
|
+
...opts.globalRoot !== void 0 ? { globalRoot: opts.globalRoot } : {}
|
|
2358
|
+
});
|
|
2359
|
+
const sessionFile = path2.join(wpaths.projectSessions, `${opts.sessionId}.jsonl`);
|
|
2360
|
+
let agents = (opts.initialAgents ?? []).map(toAgentSummary);
|
|
2361
|
+
let lastActivityAt = agents.reduce(
|
|
2362
|
+
(latest, agent) => agent.lastActivityAt > latest ? agent.lastActivityAt : latest,
|
|
2363
|
+
startedAt
|
|
2364
|
+
);
|
|
2365
|
+
let lastSnapshotHash = "";
|
|
2366
|
+
let disposed = false;
|
|
2367
|
+
function buildSnapshot() {
|
|
2368
|
+
return {
|
|
2369
|
+
sessionId: opts.sessionId,
|
|
2370
|
+
clientKind: identity.kind,
|
|
2371
|
+
machineId: identity.machineId,
|
|
2372
|
+
projectId: project.projectId,
|
|
2373
|
+
projectName: opts.projectName ?? project.projectName,
|
|
2374
|
+
projectRoot: opts.projectRoot,
|
|
2375
|
+
status: deriveSessionStatus(agents),
|
|
2376
|
+
startedAt,
|
|
2377
|
+
lastActivityAt,
|
|
2378
|
+
agentCount: agents.length,
|
|
2379
|
+
agents,
|
|
2380
|
+
...identity.hostname !== void 0 ? { hostname: identity.hostname } : {},
|
|
2381
|
+
...identity.pid !== void 0 ? { pid: identity.pid } : {},
|
|
2382
|
+
...opts.gitBranch !== void 0 ? { gitBranch: opts.gitBranch } : {}
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
function publishSnapshot(force = false) {
|
|
2386
|
+
if (disposed) return;
|
|
2387
|
+
const snap = buildSnapshot();
|
|
2388
|
+
const hash = JSON.stringify({ ...snap, lastActivityAt: "" });
|
|
2389
|
+
if (!force && hash === lastSnapshotHash) return;
|
|
2390
|
+
lastSnapshotHash = hash;
|
|
2391
|
+
try {
|
|
2392
|
+
publisher.publishSessionSnapshot(snap);
|
|
2393
|
+
} catch {
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
const offAgents = opts.events?.on("session.agents_updated", (payload) => {
|
|
2397
|
+
agents = payload.agents.map(toAgentSummary);
|
|
2398
|
+
lastActivityAt = now();
|
|
2399
|
+
publishSnapshot();
|
|
2400
|
+
});
|
|
2401
|
+
publishSnapshot(true);
|
|
2402
|
+
let offset = 0;
|
|
2403
|
+
let partial = "";
|
|
2404
|
+
let seqEmitted = 0;
|
|
2405
|
+
let tailing = false;
|
|
2406
|
+
let watcher = null;
|
|
2407
|
+
let watchPending = false;
|
|
2408
|
+
function setupWatcher() {
|
|
2409
|
+
if (disposed || watcher) return;
|
|
2410
|
+
try {
|
|
2411
|
+
const nextWatcher = syncFs.watch(sessionFile, () => {
|
|
2412
|
+
if (watchPending || disposed) return;
|
|
2413
|
+
watchPending = true;
|
|
2414
|
+
setTimeout(() => {
|
|
2415
|
+
watchPending = false;
|
|
2416
|
+
void tail();
|
|
2417
|
+
}, 25);
|
|
2418
|
+
});
|
|
2419
|
+
nextWatcher.on("error", () => {
|
|
2420
|
+
try {
|
|
2421
|
+
nextWatcher.close();
|
|
2422
|
+
} catch {
|
|
2423
|
+
}
|
|
2424
|
+
if (watcher === nextWatcher) watcher = null;
|
|
2425
|
+
});
|
|
2426
|
+
watcher = nextWatcher;
|
|
2427
|
+
} catch {
|
|
2428
|
+
watcher = null;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
async function tail() {
|
|
2432
|
+
if (disposed || tailing) return;
|
|
2433
|
+
tailing = true;
|
|
2434
|
+
try {
|
|
2435
|
+
const stat4 = await fsp.stat(sessionFile).catch(() => null);
|
|
2436
|
+
if (disposed) return;
|
|
2437
|
+
if (!stat4) return;
|
|
2438
|
+
setupWatcher();
|
|
2439
|
+
if (stat4.size <= offset) return;
|
|
2440
|
+
const fd = await fsp.open(sessionFile, "r");
|
|
2441
|
+
try {
|
|
2442
|
+
if (disposed) return;
|
|
2443
|
+
const len = stat4.size - offset;
|
|
2444
|
+
const buf = Buffer.allocUnsafe(len);
|
|
2445
|
+
await fd.read(buf, 0, len, offset);
|
|
2446
|
+
offset = stat4.size;
|
|
2447
|
+
partial += buf.toString("utf8");
|
|
2448
|
+
const lines = partial.split("\n");
|
|
2449
|
+
partial = lines.pop() ?? "";
|
|
2450
|
+
const entries = [];
|
|
2451
|
+
for (const line of lines) {
|
|
2452
|
+
const trimmed = line.trim();
|
|
2453
|
+
if (!trimmed) continue;
|
|
2454
|
+
let obj;
|
|
2455
|
+
try {
|
|
2456
|
+
obj = JSON.parse(trimmed);
|
|
2457
|
+
} catch {
|
|
2458
|
+
continue;
|
|
2459
|
+
}
|
|
2460
|
+
for (const entry of mapSessionEventToEntries(obj)) entries.push(entry);
|
|
2461
|
+
}
|
|
2462
|
+
if (entries.length > 0) {
|
|
2463
|
+
try {
|
|
2464
|
+
publisher.publishTranscriptAppend({
|
|
2465
|
+
sessionId: opts.sessionId,
|
|
2466
|
+
fromSeq: seqEmitted,
|
|
2467
|
+
entries
|
|
2468
|
+
});
|
|
2469
|
+
} catch {
|
|
2470
|
+
}
|
|
2471
|
+
seqEmitted += entries.length;
|
|
2472
|
+
lastActivityAt = now();
|
|
2473
|
+
}
|
|
2474
|
+
} finally {
|
|
2475
|
+
await fd.close();
|
|
2476
|
+
}
|
|
2477
|
+
} catch {
|
|
2478
|
+
} finally {
|
|
2479
|
+
tailing = false;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
const snapshotTimer = setInterval(() => publishSnapshot(true), opts.snapshotIntervalMs ?? 2500);
|
|
2483
|
+
const tailTimer = setInterval(() => void tail(), opts.transcriptIntervalMs ?? 500);
|
|
2484
|
+
snapshotTimer.unref?.();
|
|
2485
|
+
tailTimer.unref?.();
|
|
2486
|
+
void tail();
|
|
2487
|
+
return () => {
|
|
2488
|
+
if (disposed) return;
|
|
2489
|
+
disposed = true;
|
|
2490
|
+
offAgents?.();
|
|
2491
|
+
if (watcher) {
|
|
2492
|
+
try {
|
|
2493
|
+
watcher.close();
|
|
2494
|
+
} catch {
|
|
2495
|
+
}
|
|
2496
|
+
watcher = null;
|
|
2497
|
+
}
|
|
2498
|
+
clearInterval(snapshotTimer);
|
|
2499
|
+
clearInterval(tailTimer);
|
|
2500
|
+
try {
|
|
2501
|
+
publisher.publishSessionEnded({ sessionId: opts.sessionId, endedAt: now() });
|
|
2502
|
+
} catch {
|
|
2503
|
+
}
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
export { DEFAULT_HQ_REDACTION_POLICY, HQ_AUTH_FILE_VERSION, HQ_PROTOCOL_VERSION, HqPublisher, buildTranscriptFromEvents, createGlobalMailbox, createHqEventEnvelope, createHqPublisherFromEnv, createMailboxEventPayload, createMailboxSnapshotPayload, createMailboxSnapshotPayloadFromMailbox, defaultHqDataDir, emptyHqAuthFile, ensureHqFirstRunAuthFile, hqAuthFilePath, hqRuntimeFilePath, mapMailboxAgentToHqSummary, mapMailboxMessageToHqSummary, mapSessionEventToEntries, mergeToolResults, mintHqBrowserToken, mintHqToken, mutateHqAuthFile, parseHqEventPayload, parseHqFrame, readHqAuthFile, readHqRuntimeFileSync, redactHqEvent, redactHqValue, resolveHqConfig, resolveHqConfigFromEnv, resolveHqDataDir, scrubAndTruncateHqPreview, startAgentMonitorEventBridge, startSessionTelemetryBridge, summarizeHqToolArgs, watchHqAuthFile, writeHqAuthFile, writeHqRuntimeFile };
|
|
1930
2508
|
//# sourceMappingURL=index.js.map
|
|
1931
2509
|
//# sourceMappingURL=index.js.map
|