@hachej/boring-agent 0.1.89 → 0.1.91

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 (39) hide show
  1. package/dist/{chunk-GOM3VJOJ.js → chunk-2GBHYJVC.js} +31 -1
  2. package/dist/{chunk-I7UEASPG.js → chunk-BHH5NH7P.js} +5534 -8253
  3. package/dist/{chunk-M32FXCIN.js → chunk-CJ2BW2W2.js} +23 -8
  4. package/dist/{chunk-WZCTVT57.js → chunk-IWBC6SF7.js} +94 -112
  5. package/dist/chunk-NITBKFG6.js +1246 -0
  6. package/dist/{chunk-BDYXKHWY.js → chunk-PZC76CIA.js} +7 -1
  7. package/dist/core/index.d.ts +5 -5
  8. package/dist/core/index.js +2 -2
  9. package/dist/{createHarness-CCQLHKUF.js → createHarness-PMYRKDE4.js} +2 -2
  10. package/dist/errors-BEmw9HMp.d.ts +200 -0
  11. package/dist/front/index.d.ts +30 -5
  12. package/dist/front/index.js +230 -44
  13. package/dist/{harness-VvP0l5Mj.d.ts → harness-Bt_GMNYR.d.ts} +2 -2
  14. package/dist/{workspaceAgentDispatcher-CKEeNPjW.d.ts → lease-2aXepSBL.d.ts} +254 -59
  15. package/dist/{protocol-B_OQKfbI.d.ts → middleware-CzW8n7B7.d.ts} +20 -18
  16. package/dist/piChatEvent-DBO3M5lu.d.ts +367 -0
  17. package/dist/{piChatSessionService-DPjJ0g71.d.ts → piChatSessionService-BaaVWke7.d.ts} +5 -1
  18. package/dist/{sandbox-DthfJaOB.d.ts → sandbox-DAD8cWQR.d.ts} +14 -1
  19. package/dist/server/agent-host/testing/gatewayConformance.d.ts +41 -0
  20. package/dist/server/agent-host/testing/gatewayConformance.js +465 -0
  21. package/dist/server/index.d.ts +507 -708
  22. package/dist/server/index.js +53 -50
  23. package/dist/server/worker/index.d.ts +9 -7
  24. package/dist/server/worker/index.js +19 -28
  25. package/dist/shared/index.d.ts +119 -27
  26. package/dist/shared/index.js +65 -282
  27. package/dist/types-jcMapYr9.d.ts +759 -0
  28. package/docs/ACCESSIBILITY.md +4 -5
  29. package/docs/API.md +3 -0
  30. package/docs/AUTHORING.md +68 -0
  31. package/docs/ERROR_CODES.md +29 -0
  32. package/docs/README.md +1 -0
  33. package/docs/RISKS-MULTI-TAB.md +1 -1
  34. package/docs/runtime-provisioning.md +1 -1
  35. package/docs/runtime.md +9 -6
  36. package/package.json +18 -9
  37. package/dist/chunk-DGRKMMXO.js +0 -8
  38. package/dist/chunk-XPQEF32D.js +0 -385
  39. package/dist/piChatEvent-nmOBSNU9.d.ts +0 -367
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-WSQ5QNIY.js";
4
4
  import {
5
5
  ErrorCode
6
- } from "./chunk-GOM3VJOJ.js";
6
+ } from "./chunk-2GBHYJVC.js";
7
7
 
8
8
  // src/core/piChatSessionService.ts
9
9
  var AgentEffectAdmissionError = class extends Error {
@@ -17,6 +17,20 @@ var AgentEffectAdmissionError = class extends Error {
17
17
  details;
18
18
  statusCode = 500;
19
19
  };
20
+ var observedSynchronousServiceErrors = /* @__PURE__ */ new WeakSet();
21
+ function invokeObservedServiceEffect(effect) {
22
+ try {
23
+ return effect();
24
+ } catch (error) {
25
+ if (typeof error === "object" && error !== null || typeof error === "function") {
26
+ observedSynchronousServiceErrors.add(error);
27
+ }
28
+ throw error;
29
+ }
30
+ }
31
+ function isObservedSynchronousServiceError(error) {
32
+ return (typeof error === "object" && error !== null || typeof error === "function") && observedSynchronousServiceErrors.has(error);
33
+ }
20
34
  var AGENT_EFFECT_METHODS = {
21
35
  createSession: true,
22
36
  deleteSession: true,
@@ -31,34 +45,34 @@ function withAgentEffectAdmission(service, admit) {
31
45
  ...service.listSessions ? { listSessions: (ctx, options) => service.listSessions(ctx, options) } : {},
32
46
  async createSession(ctx, init) {
33
47
  await admit(ctx);
34
- return service.createSession(ctx, init);
48
+ return invokeObservedServiceEffect(() => service.createSession(ctx, init));
35
49
  },
36
50
  async deleteSession(ctx, sessionId) {
37
51
  await admit(ctx);
38
- return service.deleteSession(ctx, sessionId);
52
+ return invokeObservedServiceEffect(() => service.deleteSession(ctx, sessionId));
39
53
  },
40
54
  ...service.readAttachment ? { readAttachment: (ctx, sessionId, messageId, index) => service.readAttachment(ctx, sessionId, messageId, index) } : {},
41
55
  readState: (ctx, sessionId) => service.readState(ctx, sessionId),
42
56
  subscribe: (ctx, sessionId, cursor, subscriber) => service.subscribe(ctx, sessionId, cursor, subscriber),
43
57
  async prompt(ctx, sessionId, payload) {
44
58
  await admit(ctx);
45
- return service.prompt(ctx, sessionId, payload);
59
+ return invokeObservedServiceEffect(() => service.prompt(ctx, sessionId, payload));
46
60
  },
47
61
  async followUp(ctx, sessionId, payload) {
48
62
  await admit(ctx);
49
- return service.followUp(ctx, sessionId, payload);
63
+ return invokeObservedServiceEffect(() => service.followUp(ctx, sessionId, payload));
50
64
  },
51
65
  async clearQueue(ctx, sessionId, payload) {
52
66
  await admit(ctx);
53
- return service.clearQueue(ctx, sessionId, payload);
67
+ return invokeObservedServiceEffect(() => service.clearQueue(ctx, sessionId, payload));
54
68
  },
55
69
  async interrupt(ctx, sessionId, payload) {
56
70
  await admit(ctx);
57
- return service.interrupt(ctx, sessionId, payload);
71
+ return invokeObservedServiceEffect(() => service.interrupt(ctx, sessionId, payload));
58
72
  },
59
73
  async stop(ctx, sessionId, payload) {
60
74
  await admit(ctx);
61
- return service.stop(ctx, sessionId, payload);
75
+ return invokeObservedServiceEffect(() => service.stop(ctx, sessionId, payload));
62
76
  },
63
77
  ...service.dispose ? { dispose: () => service.dispose() } : {}
64
78
  };
@@ -596,6 +610,7 @@ function createLiveIterator(state, startIndex) {
596
610
 
597
611
  export {
598
612
  AgentEffectAdmissionError,
613
+ isObservedSynchronousServiceError,
599
614
  AGENT_EFFECT_METHODS,
600
615
  withAgentEffectAdmission,
601
616
  createAgent,
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-AQBXNPMD.js";
5
5
  import {
6
6
  ErrorCode
7
- } from "./chunk-GOM3VJOJ.js";
7
+ } from "./chunk-2GBHYJVC.js";
8
8
 
9
9
  // src/server/harness/pi-coding-agent/createHarness.ts
10
10
  import { AsyncLocalStorage } from "async_hooks";
@@ -20,88 +20,7 @@ import {
20
20
  getAgentDir as getAgentDir2,
21
21
  loadSkills
22
22
  } from "@mariozechner/pi-coding-agent";
23
-
24
- // src/server/logging.ts
25
- var SENSITIVE_KEYS = new Set([
26
- "apiKey",
27
- "api_key",
28
- "token",
29
- "secret",
30
- "password",
31
- "authorization",
32
- "cookie",
33
- "oidcToken",
34
- "accessToken",
35
- "refreshToken",
36
- "ANTHROPIC_API_KEY",
37
- "OPENAI_API_KEY",
38
- "VERCEL_OIDC_TOKEN",
39
- "VERCEL_TEAM_ID"
40
- ].map((key) => key.toLowerCase()));
41
- function isSensitiveKey(key) {
42
- return SENSITIVE_KEYS.has(key.toLowerCase());
43
- }
44
- function redactValue(key, value, seen) {
45
- if (key && isSensitiveKey(key) && value != null) return "***";
46
- if (value == null || typeof value !== "object") return value;
47
- if (value instanceof Date) return value;
48
- if (seen.has(value)) return "[Circular]";
49
- seen.add(value);
50
- if (Array.isArray(value)) {
51
- const out2 = value.map((item) => redactValue(void 0, item, seen));
52
- seen.delete(value);
53
- return out2;
54
- }
55
- const out = {};
56
- for (const [childKey, childValue] of Object.entries(value)) {
57
- out[childKey] = redactValue(childKey, childValue, seen);
58
- }
59
- seen.delete(value);
60
- return out;
61
- }
62
- function redact(fields) {
63
- const out = {};
64
- const seen = /* @__PURE__ */ new WeakSet();
65
- for (const [k, v] of Object.entries(fields)) {
66
- out[k] = redactValue(k, v, seen);
67
- }
68
- return out;
69
- }
70
- function isVerbose() {
71
- return typeof process !== "undefined" && process.env?.BORING_AGENT_VERBOSE === "1";
72
- }
73
- function createLogger(prefix) {
74
- function emit(level, msg, fields) {
75
- const entry = {
76
- level,
77
- prefix,
78
- msg,
79
- ...fields ? redact(fields) : {},
80
- t: (/* @__PURE__ */ new Date()).toISOString()
81
- };
82
- if (level === "error") {
83
- console.error(JSON.stringify(entry));
84
- } else if (level === "warn") {
85
- console.warn(JSON.stringify(entry));
86
- } else {
87
- console.log(JSON.stringify(entry));
88
- }
89
- }
90
- return {
91
- debug(msg, fields) {
92
- if (isVerbose()) emit("debug", msg, fields);
93
- },
94
- info(msg, fields) {
95
- emit("info", msg, fields);
96
- },
97
- warn(msg, fields) {
98
- emit("warn", msg, fields);
99
- },
100
- error(msg, fields) {
101
- emit("error", msg, fields);
102
- }
103
- };
104
- }
23
+ import { createLogger } from "@hachej/boring-bash/server";
105
24
 
106
25
  // src/server/harness/pi-coding-agent/tool-adapter.ts
107
26
  var BORING_TOOL_ERROR_MARKER = "__boringToolError";
@@ -399,14 +318,6 @@ import { homedir } from "os";
399
318
  function getEnv(name) {
400
319
  return process.env[name];
401
320
  }
402
- function getEnvSnapshot() {
403
- return { ...process.env };
404
- }
405
- function setEnvDefault(name, value) {
406
- if (process.env[name] !== void 0) return false;
407
- process.env[name] = value;
408
- return true;
409
- }
410
321
 
411
322
  // src/server/harness/pi-coding-agent/sessions.ts
412
323
  import {
@@ -449,6 +360,7 @@ var PiSessionStore = class {
449
360
  allowLegacyUnscopedAccess;
450
361
  prefixCache = /* @__PURE__ */ new Map();
451
362
  listInFlight = /* @__PURE__ */ new Map();
363
+ writerTails = /* @__PURE__ */ new Map();
452
364
  constructor(cwd, options) {
453
365
  this.cwd = cwd;
454
366
  if (typeof options === "string") {
@@ -462,6 +374,14 @@ var PiSessionStore = class {
462
374
  getSessionDir() {
463
375
  return this.sessionDir;
464
376
  }
377
+ /** Reads the Host execution pin from authoritative session metadata. */
378
+ async readRuntimeScopeIdentity(ctx, sessionId) {
379
+ const filepath = await this.resolveSessionFile(sessionId, ctx);
380
+ const entries = parseJsonlPrefixEntries(await readJsonlPrefix(filepath));
381
+ const header = entries.find((entry) => entry.type === "session");
382
+ if (!this.headerBelongsToCtx(header, ctx)) throw new Error(`Session not found: ${sessionId}`);
383
+ return readHeaderRuntimeScopeIdentity(header);
384
+ }
465
385
  async list(ctx, options) {
466
386
  const normalizedOptions = normalizeListOptions(options);
467
387
  const inFlightKey = JSON.stringify([
@@ -541,7 +461,7 @@ var PiSessionStore = class {
541
461
  }
542
462
  async load(ctx, sessionId) {
543
463
  const resolved = await this.resolveSessionTranscript(ctx, sessionId);
544
- const title = extractTitle(resolved.sessionEntries) ?? extractTitle(resolved.linkedEntries) ?? "New session";
464
+ const title = newestDurableTitle(resolved.sessionEntries, resolved.linkedEntries) ?? "New session";
545
465
  const turnCount = countUserTurns(resolved.transcriptEntries);
546
466
  const updatedAtMs = Math.max(resolved.fileStat.mtime.getTime(), resolved.linkedMtimeMs ?? 0);
547
467
  return {
@@ -616,9 +536,53 @@ var PiSessionStore = class {
616
536
  linkedEntries,
617
537
  transcriptEntries,
618
538
  fileStat,
619
- linkedMtimeMs: linked?.mtime.getTime()
539
+ linkedMtimeMs: linked?.mtime.getTime(),
540
+ filepath,
541
+ ...linkedPiFile && linked ? { linkedFilepath: linkedPiFile } : {}
620
542
  };
621
543
  }
544
+ /**
545
+ * Addressed rename capability. Every resolved wrapper/native transcript is
546
+ * appended through the same JSONL path under one process-local writer lock.
547
+ */
548
+ async rename(ctx, sessionId, title) {
549
+ const trimmed = title.trim();
550
+ if (!trimmed) throw new Error("Session title must not be empty");
551
+ return await this.withWriter(sessionId, async () => {
552
+ const resolved = await this.resolveSessionTranscript(ctx, sessionId);
553
+ const entry = {
554
+ type: "session_info",
555
+ id: randomUUID(),
556
+ parentId: null,
557
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
558
+ name: trimmed
559
+ };
560
+ const line = `${JSON.stringify(entry)}
561
+ `;
562
+ const targets = [resolved.filepath, resolved.linkedFilepath].filter((path) => Boolean(path));
563
+ for (const filepath of targets) {
564
+ await appendFile(filepath, line);
565
+ this.prefixCache.delete(filepath);
566
+ }
567
+ return await this.load(ctx, sessionId);
568
+ });
569
+ }
570
+ async withWriter(key, action) {
571
+ const previous = this.writerTails.get(key) ?? Promise.resolve();
572
+ let release;
573
+ const current = new Promise((resolve2) => {
574
+ release = resolve2;
575
+ });
576
+ const tail = previous.then(() => current);
577
+ this.writerTails.set(key, tail);
578
+ await previous;
579
+ try {
580
+ return await action();
581
+ } finally {
582
+ release();
583
+ if (this.writerTails.get(key) === tail) this.writerTails.delete(key);
584
+ }
585
+ }
622
586
  // Synchronous variant used during session initialization so that no async
623
587
  // I/O hop is introduced before createAgentSession (which would break test
624
588
  // timing when fake timers are in use). The file is tiny (metadata only).
@@ -822,7 +786,7 @@ var PiSessionStore = class {
822
786
  const linkedEntries = linked?.entries.filter(
823
787
  (e) => e.type !== "session"
824
788
  ) ?? [];
825
- const title = extractTitle(sessionEntries) ?? extractTitle(linkedEntries) ?? firstUserMessage(linkedEntries) ?? firstUserMessage(sessionEntries) ?? "New session";
789
+ const title = newestDurableTitle(sessionEntries, linkedEntries) ?? firstUserMessage(linkedEntries) ?? firstUserMessage(sessionEntries) ?? "New session";
826
790
  const turnCount = [...sessionEntries, ...linkedEntries].filter(
827
791
  (e) => e.type === "message" && e.message?.role === "user"
828
792
  ).length;
@@ -1061,11 +1025,19 @@ function readHeaderSessionCtx(header) {
1061
1025
  if (!raw || typeof raw !== "object") return {};
1062
1026
  return normalizeSessionCtx(raw) ?? {};
1063
1027
  }
1028
+ function readHeaderRuntimeScopeIdentity(header) {
1029
+ const raw = header?.boringSessionCtx?.runtimeScopeIdentity;
1030
+ if (raw === void 0) return void 0;
1031
+ if (typeof raw !== "string" || !raw.trim()) throw new Error("Session runtime scope identity is invalid");
1032
+ return raw;
1033
+ }
1064
1034
  function normalizeSessionCtx(ctx) {
1065
- if (!ctx?.workspaceId && !ctx?.userId) return void 0;
1035
+ const runtimeScopeIdentity = typeof ctx?.runtimeScopeIdentity === "string" && ctx.runtimeScopeIdentity.trim() ? ctx.runtimeScopeIdentity : "";
1036
+ if (!ctx?.workspaceId && !ctx?.userId && !runtimeScopeIdentity) return void 0;
1066
1037
  return {
1067
- ...ctx.workspaceId ? { workspaceId: ctx.workspaceId } : {},
1068
- ...ctx.userId ? { userId: ctx.userId } : {}
1038
+ ...ctx?.workspaceId ? { workspaceId: ctx.workspaceId } : {},
1039
+ ...ctx?.userId ? { userId: ctx.userId } : {},
1040
+ ...runtimeScopeIdentity ? { runtimeScopeIdentity } : {}
1069
1041
  };
1070
1042
  }
1071
1043
  function sameSessionCtx(a, b) {
@@ -1109,9 +1081,16 @@ function countUserTurns(entries) {
1109
1081
  (e) => e.type === "message" && e.message?.role === "user"
1110
1082
  ).length;
1111
1083
  }
1112
- function extractTitle(entries) {
1113
- const last = entries.filter((e) => e.type === "session_info").pop();
1114
- return last?.name;
1084
+ function newestDurableTitle(wrapperEntries, nativeEntries) {
1085
+ const candidates = [
1086
+ ...wrapperEntries.flatMap((entry, offset) => entry.type === "session_info" ? [{ entry, native: 0, offset }] : []),
1087
+ ...nativeEntries.flatMap((entry, offset) => entry.type === "session_info" ? [{ entry, native: 1, offset }] : [])
1088
+ ];
1089
+ candidates.sort((a, b) => {
1090
+ const timestamp = Date.parse(b.entry.timestamp) - Date.parse(a.entry.timestamp);
1091
+ return timestamp || b.native - a.native || b.offset - a.offset;
1092
+ });
1093
+ return candidates[0]?.entry.name;
1115
1094
  }
1116
1095
  function firstUserMessage(entries) {
1117
1096
  for (const e of entries) {
@@ -1208,6 +1187,12 @@ function clean(value) {
1208
1187
  const trimmed = value?.trim();
1209
1188
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
1210
1189
  }
1190
+ function parseEncodedModelSelection(value) {
1191
+ const encoded = clean(value);
1192
+ if (!encoded) return void 0;
1193
+ const idx = encoded.indexOf(":");
1194
+ return idx > 0 && idx < encoded.length - 1 ? { provider: encoded.slice(0, idx), id: encoded.slice(idx + 1) } : void 0;
1195
+ }
1211
1196
  function readPositiveInt(name, fallback) {
1212
1197
  const raw = clean(getEnv(name));
1213
1198
  if (!raw) return fallback;
@@ -1371,13 +1356,8 @@ function readPiSettingsDefaultModel() {
1371
1356
  }
1372
1357
  }
1373
1358
  function readConfiguredDefaultModel() {
1374
- const encoded = clean(getEnv("BORING_AGENT_DEFAULT_MODEL"));
1375
- if (encoded) {
1376
- const idx = encoded.indexOf(":");
1377
- if (idx > 0 && idx < encoded.length - 1) {
1378
- return { provider: encoded.slice(0, idx), id: encoded.slice(idx + 1) };
1379
- }
1380
- }
1359
+ const encoded = parseEncodedModelSelection(getEnv("BORING_AGENT_DEFAULT_MODEL"));
1360
+ if (encoded) return encoded;
1381
1361
  const explicitProvider = clean(getEnv("BORING_AGENT_DEFAULT_MODEL_PROVIDER"));
1382
1362
  const explicitId = clean(getEnv("BORING_AGENT_DEFAULT_MODEL_ID"));
1383
1363
  if (explicitProvider && explicitId) {
@@ -1515,7 +1495,10 @@ function resolveRequestedModel(modelRegistry, input, options = {}) {
1515
1495
  }
1516
1496
  return model;
1517
1497
  }
1518
- function resolveDefaultModel(modelRegistry) {
1498
+ function resolveDefaultModel(modelRegistry, override, strict) {
1499
+ if (override) {
1500
+ return resolveRequestedModel(modelRegistry, { model: override }, { strict });
1501
+ }
1519
1502
  const configured = readConfiguredDefaultModel();
1520
1503
  if (configured) {
1521
1504
  const model = modelRegistry.find(configured.provider, configured.id);
@@ -1725,7 +1708,7 @@ function createPiCodingAgentHarness(opts) {
1725
1708
  isNewPiSession = true;
1726
1709
  }
1727
1710
  const resolvedModel = resolveRequestedModel(modelRegistry, input, { strict: pi.strictModelResolution });
1728
- const model = resolvedModel ?? resolveDefaultModel(modelRegistry);
1711
+ const model = resolvedModel ?? resolveDefaultModel(modelRegistry, pi.defaultModel, pi.strictModelResolution);
1729
1712
  refreshEffectiveResources();
1730
1713
  const composedSystemPromptAppend = composeSystemPromptAppend(opts.systemPromptAppend);
1731
1714
  const dynamicPromptExtension = opts.systemPromptDynamic ? buildDynamicPromptExtension(opts.systemPromptDynamic) : void 0;
@@ -1928,9 +1911,8 @@ ${entry.message}`;
1928
1911
 
1929
1912
  export {
1930
1913
  getEnv,
1931
- getEnvSnapshot,
1932
- setEnvDefault,
1933
- createLogger,
1914
+ PiSessionStore,
1915
+ parseEncodedModelSelection,
1934
1916
  registerConfiguredModelProviders,
1935
1917
  readConfiguredDefaultModel,
1936
1918
  PI_PACKAGE_RESOURCE_FILTERS,