@ordewell/core 0.10.10 → 0.10.12

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.mjs CHANGED
@@ -21628,9 +21628,11 @@ function plannerNotReadyFor(config) {
21628
21628
  var TERMINAL_UI_COMMANDS = {
21629
21629
  pickPlanner: "/planner",
21630
21630
  setKey: () => "/key",
21631
- setModel: "/model"
21631
+ // `/planner` walks harness, model and effort in one flow; there is no
21632
+ // separate `/model` (or `/planner-effort`) to point at.
21633
+ setModel: "/planner"
21632
21634
  };
21633
- var CHAT_COMMANDS = { ...TERMINAL_UI_COMMANDS, setModel: "/model set" };
21635
+ var CHAT_COMMANDS = { ...TERMINAL_UI_COMMANDS };
21634
21636
  var HEADLESS_CLI_COMMANDS = {
21635
21637
  pickPlanner: "ordewell planner <id>",
21636
21638
  setKey: (provider) => `ordewell key set ${provider} <key>`,
@@ -21644,7 +21646,7 @@ function harnessPlannerNames() {
21644
21646
  var PLANNER_ESCAPE_HINT = `or pick a coding agent with ${TERMINAL_UI_COMMANDS.pickPlanner}: it plans with no API key`;
21645
21647
  function describePlannerNotReady(reason, commands) {
21646
21648
  if (reason.code === "no_model") {
21647
- return `No planner model selected for ${reason.label}. Pick one with ${commands.setModel}, or switch planner with ${commands.pickPlanner}.`;
21649
+ return commands.setModel === commands.pickPlanner ? `No planner model selected for ${reason.label}. Pick one with ${commands.setModel}.` : `No planner model selected for ${reason.label}. Pick one with ${commands.setModel}, or switch planner with ${commands.pickPlanner}.`;
21648
21650
  }
21649
21651
  const lead = `No planner is ready. Pick one with ${commands.pickPlanner}: ${harnessPlannerNames()} plan with no API key.`;
21650
21652
  if (reason.code === "no_base_url") {
@@ -32377,6 +32379,9 @@ var StructuredSession = class extends AbstractRunnerSession {
32377
32379
  text;
32378
32380
  state = "idle";
32379
32381
  turn = null;
32382
+ /** A `task_complete` report waiting for its turn to close; the verdict is published then. */
32383
+ completionReport = null;
32384
+ completionTimer = null;
32380
32385
  /** A terminal event ends the turn before the adapter releases its send. */
32381
32386
  sending = null;
32382
32387
  turnCount = 0;
@@ -32385,6 +32390,8 @@ var StructuredSession = class extends AbstractRunnerSession {
32385
32390
  interrupting = null;
32386
32391
  /** The wind-down once {@link stop} was asked for: the same for every caller, and nothing new is sent meanwhile. */
32387
32392
  stopping = null;
32393
+ /** The process is handling its exit: nothing new may be queued, so a held report can still publish. */
32394
+ exiting = false;
32388
32395
  lastSessionId = null;
32389
32396
  lastSessionCost = null;
32390
32397
  structuredEmitter = new EventEmitter2();
@@ -32435,7 +32442,9 @@ var StructuredSession = class extends AbstractRunnerSession {
32435
32442
  create: () => this.launch.createAdapter(this.launch.runner, this.launch.deps),
32436
32443
  issueToken: () => server4.issueTaskToken(scope, {
32437
32444
  taskComplete: async (report) => {
32438
- this.structuredEmitter.emit("taskComplete", report);
32445
+ this.completionReport = report;
32446
+ if (this.completionTimer) clearTimeout(this.completionTimer);
32447
+ this.completionTimer = setTimeout(() => this.flushCompletion(), this.launch.interruptGraceMs);
32439
32448
  return { text: "Recorded. End your turn now." };
32440
32449
  },
32441
32450
  checkpoint: async ({ question }, { signal }) => {
@@ -32467,6 +32476,9 @@ var StructuredSession = class extends AbstractRunnerSession {
32467
32476
  this.toolToken = null;
32468
32477
  }
32469
32478
  baseHandleExit(code) {
32479
+ this.exiting = true;
32480
+ this.undeliverQueued();
32481
+ this.flushCompletion();
32470
32482
  this.ended.abort();
32471
32483
  this.revokeTools();
32472
32484
  this.undeliverQueued();
@@ -32562,7 +32574,7 @@ var StructuredSession = class extends AbstractRunnerSession {
32562
32574
  this.messageCount += 1;
32563
32575
  const id = `msg-${this.messageCount}`;
32564
32576
  const sender2 = opening.from ? { from: opening.from } : {};
32565
- if (this.exited || this.stopping) this.emitEvent({ type: "message_undelivered", messageId: id, text, ...sender2 });
32577
+ if (this.exited || this.exiting || this.stopping) this.emitEvent({ type: "message_undelivered", messageId: id, text, ...sender2 });
32566
32578
  else if (this.state === "idle" && this.adapterStarted && !this.sending) this.deliver(text, opening);
32567
32579
  else {
32568
32580
  this.state = "working";
@@ -32617,7 +32629,7 @@ var StructuredSession = class extends AbstractRunnerSession {
32617
32629
  });
32618
32630
  }
32619
32631
  runningTurn() {
32620
- return !this.exited && !this.stopping && this.adapter !== null && this.turn !== null && !this.turn.ended;
32632
+ return !this.exited && !this.exiting && !this.stopping && this.adapter !== null && this.turn !== null && !this.turn.ended;
32621
32633
  }
32622
32634
  /** No runner can recall a message once offered to it, so only a queued one comes back. */
32623
32635
  removeQueued(id) {
@@ -32748,6 +32760,7 @@ var StructuredSession = class extends AbstractRunnerSession {
32748
32760
  /** `message` is what opened the turn: a queued one has an id, one delivered straight from idle only its sender. */
32749
32761
  openTurn(text, message) {
32750
32762
  this.turnCount += 1;
32763
+ this.discardCompletion();
32751
32764
  const turn = { id: this.turnCount, abort: new AbortController(), ended: false, reason: "completed", steerRefused: false };
32752
32765
  this.turn = turn;
32753
32766
  this.state = "working";
@@ -32796,7 +32809,7 @@ var StructuredSession = class extends AbstractRunnerSession {
32796
32809
  offerNext() {
32797
32810
  const adapter = this.adapter;
32798
32811
  const turn = this.turn;
32799
- if (!adapter?.steer || !turn || turn.ended || turn.steerRefused || this.interrupting || this.exited || this.stopping) return;
32812
+ if (!adapter?.steer || !turn || turn.ended || turn.steerRefused || this.interrupting || this.exited || this.exiting || this.stopping) return;
32800
32813
  if (this.queue.some((m) => m.stage === "steering")) return;
32801
32814
  const next = this.queue.find((m) => m.stage === "queued");
32802
32815
  if (!next || next.command) return;
@@ -32840,6 +32853,7 @@ var StructuredSession = class extends AbstractRunnerSession {
32840
32853
  } else if (event.type === "permission_request") this.openPermission(adapter, event);
32841
32854
  else if (event.type === "permission_cancelled") this.cancelPermission(adapter, event.id);
32842
32855
  else if (event.type === "message_delivered") {
32856
+ this.discardCompletion();
32843
32857
  const message = this.delivered(event.id);
32844
32858
  if (message) this.emitEvent({ type: "message_delivered", messageId: message.id, text: message.text, ...message.from ? { from: message.from } : {} });
32845
32859
  } else if (event.type === "message_dropped") {
@@ -32920,6 +32934,24 @@ var StructuredSession = class extends AbstractRunnerSession {
32920
32934
  }
32921
32935
  this.emitEvent(event);
32922
32936
  }
32937
+ /** Publish a `task_complete` report whose turn has closed, or that waited out the grace. */
32938
+ flushCompletion() {
32939
+ if (this.completionTimer) {
32940
+ clearTimeout(this.completionTimer);
32941
+ this.completionTimer = null;
32942
+ }
32943
+ const report = this.completionReport;
32944
+ this.completionReport = null;
32945
+ if (report) this.structuredEmitter.emit("taskComplete", report);
32946
+ }
32947
+ /** A message the runner reads, or a turn it opens, voids a report made before it (ADR-0023, D6). */
32948
+ discardCompletion() {
32949
+ if (this.completionTimer) {
32950
+ clearTimeout(this.completionTimer);
32951
+ this.completionTimer = null;
32952
+ }
32953
+ this.completionReport = null;
32954
+ }
32923
32955
  /**
32924
32956
  * A queued message goes out as the turn closes, and the state never passes
32925
32957
  * through `idle` on the way, so a listener told the turn ended can already
@@ -32932,6 +32964,7 @@ var StructuredSession = class extends AbstractRunnerSession {
32932
32964
  turn.ended = true;
32933
32965
  this.text.endTurn();
32934
32966
  this.emitEvent({ type: "turn_end", reason });
32967
+ this.flushCompletion();
32935
32968
  if (reason === "failed") this.undeliverQueued();
32936
32969
  this.state = !this.exited && this.queue.length ? "working" : "idle";
32937
32970
  this.structuredEmitter.emit("turnEnd", reason);
@@ -32942,7 +32975,7 @@ var StructuredSession = class extends AbstractRunnerSession {
32942
32975
  * back in Ordewell's hands: while it is on offer or owed, nothing overtakes it.
32943
32976
  */
32944
32977
  deliverNext() {
32945
- if (this.exited || this.stopping || this.sending || this.turn && !this.turn.ended) return;
32978
+ if (this.exited || this.exiting || this.stopping || this.sending || this.turn && !this.turn.ended) return;
32946
32979
  const head = this.queue[0];
32947
32980
  if (!head || this.queue.some((message) => message.stage !== "queued")) return;
32948
32981
  this.queue.shift();