@epoch-agent/server 0.11.0 → 0.13.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.
package/dist/index.js CHANGED
@@ -197,6 +197,7 @@ var SessionRegistry = class {
197
197
  controller: null,
198
198
  turnStartedAt: null,
199
199
  lastFinish: null,
200
+ persisted: null,
200
201
  queue: [],
201
202
  lastActiveTick: ++this.tick
202
203
  });
@@ -465,6 +466,9 @@ var SessionHub = class {
465
466
  isCooled(sessionId) {
466
467
  return this.registry.isCooled(sessionId);
467
468
  }
469
+ persistedThrough(sessionId) {
470
+ return this.registry.get(sessionId)?.persisted ?? null;
471
+ }
468
472
  stateOf(sessionId) {
469
473
  return this.registry.get(sessionId)?.state ?? "idle";
470
474
  }
@@ -542,21 +546,22 @@ var SessionHub = class {
542
546
  }
543
547
  const queued = { message, ...decorate ? { decorate } : {} };
544
548
  if (entry.state !== "idle") {
549
+ const steered = decorate === void 0 && entry.queue.every((q) => q.decorate === void 0);
545
550
  if (!this.registry.enqueue(entry, queued)) {
546
551
  return { ok: false, reason: "queue-full", state: entry.state };
547
552
  }
548
- return { ok: true, queued: true };
553
+ return { ok: true, queued: true, steered };
549
554
  }
550
555
  this.beginTurn(entry, queued);
551
556
  return { ok: true, queued: false };
552
557
  }
553
558
  abort(sessionId) {
554
559
  const entry = this.registry.get(sessionId);
555
- if (!entry) return false;
556
- entry.queue.length = 0;
557
- if (!entry.controller) return false;
560
+ if (!entry) return { running: false, returned: [] };
561
+ const returned = entry.queue.splice(0, entry.queue.length).map((q) => q.message);
562
+ if (!entry.controller) return { running: false, returned };
558
563
  entry.controller.abort();
559
- return true;
564
+ return { running: true, returned };
560
565
  }
561
566
  listPending(sessionId) {
562
567
  return this.registry.get(sessionId)?.relay.listPending() ?? [];
@@ -637,12 +642,19 @@ var SessionHub = class {
637
642
  const sessionId = entry.session.sessionId;
638
643
  const signal = entry.controller?.signal;
639
644
  try {
640
- const run = entry.session.run(queued.message, signal ? { signal } : {});
645
+ const run = entry.session.run(queued.message, {
646
+ ...signal ? { signal } : {},
647
+ steer: this.steerFrom(entry)
648
+ });
641
649
  const raw = queued.decorate ? queued.decorate(run) : run;
642
650
  for await (const event of serializeStream(raw, entry.relay, entry.questions)) {
643
651
  const blocking = event.type === "approval-request" || event.type === "question";
644
652
  if (blocking) this.requestOwner.set(event.requestId, sessionId);
645
653
  const frame = this.publish(sessionId, stripArtifactData(event));
654
+ const through = entry.session.persistedThrough?.() ?? null;
655
+ if (through !== null && through !== entry.persisted?.id) {
656
+ entry.persisted = { id: through, seq: frame.seq };
657
+ }
646
658
  if (event.type === "finish") {
647
659
  entry.lastFinish = toWireFinish(event, frame.ts, entry.turnStartedAt);
648
660
  }
@@ -657,10 +669,22 @@ var SessionHub = class {
657
669
  }
658
670
  this.registry.touch(sessionId);
659
671
  entry.turnStartedAt = null;
672
+ entry.persisted = null;
660
673
  this.setState(entry, "idle");
661
674
  this.drainQueue(entry);
662
675
  }
663
676
  }
677
+ steerFrom(entry) {
678
+ const runLength = () => {
679
+ let n = 0;
680
+ while (n < entry.queue.length && entry.queue[n].decorate === void 0) n += 1;
681
+ return n;
682
+ };
683
+ return {
684
+ pending: () => runLength() > 0,
685
+ take: () => entry.queue.splice(0, runLength()).map((q) => q.message)
686
+ };
687
+ }
664
688
  drainQueue(entry) {
665
689
  if (this.closed || !this.registry.has(entry.session.sessionId)) return;
666
690
  if (this.present === 0) {
@@ -1503,9 +1527,13 @@ function toWireSettingRow(row) {
1503
1527
  overridable: row.overridable,
1504
1528
  writes: row.writes.map(toWireSettingWrite),
1505
1529
  ...row.valueKind === void 0 ? {} : { valueKind: row.valueKind },
1506
- ...row.choices === void 0 ? {} : { choices: [...row.choices] }
1530
+ ...row.choices === void 0 ? {} : { choices: [...row.choices] },
1531
+ ...row.pending === void 0 ? {} : { pending: toWireSettingPending(row.pending) }
1507
1532
  };
1508
1533
  }
1534
+ function toWireSettingPending(pending) {
1535
+ return { ...toWireSettingWrite(pending), value: pending.value };
1536
+ }
1509
1537
  function toWireSettingStep(step) {
1510
1538
  return { layer: step.layer, ...step.value === void 0 ? {} : { value: step.value } };
1511
1539
  }
@@ -1720,8 +1748,10 @@ async function patchSession(ctx, req, res, sessionId, lang) {
1720
1748
  );
1721
1749
  }
1722
1750
  let row = wantsTitle ? catalog.rename(sessionId, title) : catalog.get(sessionId);
1751
+ const archivedBefore = row?.archived;
1723
1752
  if (row && wantsArchive) row = catalog.setArchived(sessionId, rawArchived);
1724
1753
  if (!row) return sendUnknownSession(res, sessionId, lang);
1754
+ if (wantsArchive && archivedBefore !== row.archived) ctx.hub.notifySessionsChanged(sessionId);
1725
1755
  const hub = ctx.hub.listSessions().find((s) => s.id === sessionId);
1726
1756
  sendJson(res, 200, {
1727
1757
  session: toWireSummary(sessionId, hub, row, {
@@ -1734,11 +1764,17 @@ function listMessages(ctx, res, sessionId, lang) {
1734
1764
  if (!ctx.hub.has(sessionId) && !ctx.runtime.sessions?.get(sessionId)) {
1735
1765
  return sendUnknownSession(res, sessionId, lang);
1736
1766
  }
1737
- const messages = ctx.runtime.sessionStore?.loadMessages(sessionId) ?? [];
1767
+ const all = ctx.runtime.sessionStore?.loadMessages(sessionId) ?? [];
1738
1768
  const usage = ctx.runtime.sessionUsage(sessionId);
1769
+ const flight = ctx.hub.persistedThrough(sessionId);
1770
+ const openFrom = ctx.runtime.sessionStore?.openTurnFrom(sessionId) ?? null;
1771
+ const messages = flight ? all.filter((m) => m.id <= flight.id) : all;
1772
+ const interrupted = flight === null && openFrom !== null ? openFrom : void 0;
1739
1773
  sendJson(res, 200, {
1740
1774
  messages,
1741
- ...usage ? { usage } : {}
1775
+ ...usage ? { usage } : {},
1776
+ ...flight ? { inFlightThroughSeq: flight.seq } : {},
1777
+ ...interrupted !== void 0 ? { interruptedTurnFrom: interrupted } : {}
1742
1778
  });
1743
1779
  }
1744
1780
  async function postMessage(ctx, req, res, sessionId, lang) {
@@ -1772,6 +1808,7 @@ async function postMessage(ctx, req, res, sessionId, lang) {
1772
1808
  return sendJson(res, 202, {
1773
1809
  accepted: true,
1774
1810
  queued: started.queued,
1811
+ ...started.steered === void 0 ? {} : { steered: started.steered },
1775
1812
  ...expansion.notice ? { command: expansion.notice } : {},
1776
1813
  ...attached.references.length > 0 ? { references: attached.references } : {},
1777
1814
  ...attached.files.length > 0 ? { files: attached.files } : {}
@@ -1793,7 +1830,11 @@ function pickTurnRole(live, raw) {
1793
1830
  }
1794
1831
  function abortSession(ctx, res, sessionId, lang) {
1795
1832
  if (!ctx.hub.has(sessionId)) return sendUnknownSession(res, sessionId, lang);
1796
- sendJson(res, 200, { aborted: ctx.hub.abort(sessionId) });
1833
+ const stopped = ctx.hub.abort(sessionId);
1834
+ sendJson(res, 200, {
1835
+ aborted: stopped.running,
1836
+ ...stopped.returned.length > 0 ? { returned: stopped.returned } : {}
1837
+ });
1797
1838
  }
1798
1839
  function listApprovals(ctx, res, sessionId, lang) {
1799
1840
  if (!ctx.hub.has(sessionId)) return sendUnknownSession(res, sessionId, lang);
@@ -2108,6 +2149,9 @@ async function rewind(ctx, req, res, sessionId, lang) {
2108
2149
  const input = readRewindInput(body.value, lang);
2109
2150
  if (!input.ok) return sendError(res, 400, input.code, input.message);
2110
2151
  const { turnIndex, scope, overwrite } = input.value;
2152
+ if (scope !== "files" && ctx.hub.stateOf(sessionId) !== "idle") {
2153
+ return sendError(res, 409, "turn-running", t("web.rewind_turn_running", void 0, lang));
2154
+ }
2111
2155
  const result = await control.rewind(turnIndex, {
2112
2156
  scope,
2113
2157
  ...overwrite === void 0 ? {} : { overwrite }
@@ -3192,6 +3236,39 @@ function addFailure2(reason, detail, lang) {
3192
3236
  function stringList2(value) {
3193
3237
  return value.filter((v) => typeof v === "string");
3194
3238
  }
3239
+ async function removeRole(ctx, req, res, lang) {
3240
+ if (ctx.lanExposed) {
3241
+ return sendError(
3242
+ res,
3243
+ 403,
3244
+ "role-remove-lan-exposed",
3245
+ t("web.role_remove_lan_exposed", void 0, lang)
3246
+ );
3247
+ }
3248
+ const body = await readJsonBody(req);
3249
+ if (!body.ok) return sendError(res, body.status, "bad-body", body.message);
3250
+ const payload = asRecord(body.value);
3251
+ const name = typeof payload["name"] === "string" ? payload["name"].trim() : "";
3252
+ const outcome = ctx.runtime.roleWrite.remove(name, lang);
3253
+ if (!outcome.ok) {
3254
+ return sendJson(res, 200, {
3255
+ ok: false,
3256
+ name: "",
3257
+ path: null,
3258
+ takenOverBy: null,
3259
+ detail: outcome.detail,
3260
+ reason: outcome.reason
3261
+ });
3262
+ }
3263
+ sendJson(res, 200, {
3264
+ ok: true,
3265
+ name: outcome.name,
3266
+ path: outcome.path,
3267
+ takenOverBy: outcome.takenOverBy,
3268
+ detail: null,
3269
+ reason: null
3270
+ });
3271
+ }
3195
3272
  function stringList3(value) {
3196
3273
  if (!Array.isArray(value)) return null;
3197
3274
  const out = [];
@@ -4837,6 +4914,9 @@ async function handleApi(opts, req, res, method, segments, lang) {
4837
4914
  if (resource === "roles" && first === void 0 && method === "POST") {
4838
4915
  return addRole(ctx, req, res, lang);
4839
4916
  }
4917
+ if (resource === "roles" && first === "remove" && method === "POST") {
4918
+ return removeRole(ctx, req, res, lang);
4919
+ }
4840
4920
  if (resource === "skills" && first === "import" && second === "preview" && method === "POST") {
4841
4921
  return previewSkillImport(ctx, req, res, lang);
4842
4922
  }