@fastagent-sh/fastagent 0.24.3 → 0.24.4

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.
@@ -11,7 +11,7 @@ import { resolveModel } from "./config.js";
11
11
  import { MissingModel, activePath, resolveSessionSettings } from "./session-settings.js";
12
12
  import { DEFAULT_THINKING_LEVEL, withModelRegistration } from "./models.js";
13
13
  import { registerAccountModels } from "./openai-account-models.js";
14
- import { asksToEndRun, markEndsRun } from "./turn-kit.js";
14
+ import { abortedAnswer, asksToEndRun, markEndsRun } from "./turn-kit.js";
15
15
  import { agentSessionManager, sessionToolActivation, turnContext } from "./tool-context.js";
16
16
  /**
17
17
  * What pi's own coding tools say about themselves in its default prompt. The coding tools fastagent mounts are pi's
@@ -217,6 +217,7 @@ export function definitionResourceLoaderOptions(source) {
217
217
  ...nativeExtensions,
218
218
  compactAdmission,
219
219
  recordEndsRun,
220
+ recordAbortedAnswers,
220
221
  ...(definition.sections && Object.keys(definition.sections).length > 0
221
222
  ? [fastagentSections(definition.sections)]
222
223
  : []),
@@ -279,6 +280,23 @@ const recordEndsRun = {
279
280
  });
280
281
  },
281
282
  };
283
+ /**
284
+ * Records an answer that failed because its run was stopped as aborted ({@link abortedAnswer}), through pi's
285
+ * `message_end` replacement, which runs before pi appends the message to the record and before the plane's own
286
+ * listener sees it. `ctx.signal` is the run's own abort signal, the one pi's readings use.
287
+ */
288
+ const recordAbortedAnswers = {
289
+ name: "fastagent-record-aborted-answers",
290
+ hidden: true,
291
+ factory: (pi) => {
292
+ pi.on("message_end", (event, ctx) => {
293
+ if (event.message.role !== "assistant")
294
+ return;
295
+ const aborted = abortedAnswer(event.message, ctx.signal);
296
+ return aborted ? { message: aborted } : undefined;
297
+ });
298
+ },
299
+ };
282
300
  /** Manual compactions waiting for pi's admission, keyed by the session's record (what an extension's `ctx` names). */
283
301
  const compactAdmissions = new WeakMap();
284
302
  const COMPACT_ADMISSION = "fastagent-compact-admission";
@@ -18,6 +18,15 @@ export declare function classifyRetryable(details: string, signal: {
18
18
  * the recorded one cannot disagree.
19
19
  */
20
20
  export declare function answerOutcome(message: AssistantMessage): AnswerOutcome | undefined;
21
+ /**
22
+ * An answer that failed while its run's abort was in effect, as what it was: aborted. Undefined when that is not the
23
+ * case (no abort, or not a failure). pi's own reading is `signal.aborted ? "aborted" : "error"` (`handleRunFailure`,
24
+ * the providers), but its request-setup path (`lazyStream`: auth, request preparation) writes "error" whatever the
25
+ * signal (pi 0.99.2). A run stopped during a tool lands there: the loop asks the model once more, and the setup throws
26
+ * the abort. Recorded this way, the answer's {@link answerOutcome} is the `aborted` the run settled with, live and read
27
+ * back. A failure recorded BEFORE the abort (a provider error whose retry was then stopped) stays as recorded.
28
+ */
29
+ export declare function abortedAnswer(message: AssistantMessage, runSignal: AbortSignal | undefined): AssistantMessage | undefined;
21
30
  /**
22
31
  * An answer's recorded thinking: its published thinking blocks, in order, joined with nothing between them, exactly as
23
32
  * their live deltas add up (the stream marks no block boundary, and the answer's `text` is joined the same way).
@@ -91,6 +91,19 @@ export function answerOutcome(message) {
91
91
  return undefined;
92
92
  }
93
93
  }
94
+ /**
95
+ * An answer that failed while its run's abort was in effect, as what it was: aborted. Undefined when that is not the
96
+ * case (no abort, or not a failure). pi's own reading is `signal.aborted ? "aborted" : "error"` (`handleRunFailure`,
97
+ * the providers), but its request-setup path (`lazyStream`: auth, request preparation) writes "error" whatever the
98
+ * signal (pi 0.99.2). A run stopped during a tool lands there: the loop asks the model once more, and the setup throws
99
+ * the abort. Recorded this way, the answer's {@link answerOutcome} is the `aborted` the run settled with, live and read
100
+ * back. A failure recorded BEFORE the abort (a provider error whose retry was then stopped) stays as recorded.
101
+ */
102
+ export function abortedAnswer(message, runSignal) {
103
+ return message.stopReason === "error" && runSignal?.aborted === true
104
+ ? { ...message, stopReason: "aborted" }
105
+ : undefined;
106
+ }
94
107
  /**
95
108
  * Whether a thinking block is PUBLISHED: readable reasoning. A redacted one holds a provider's opaque payload and a
96
109
  * placeholder (pi writes "[Reasoning redacted]" for it), not reasoning anyone can read, so it is neither streamed nor
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastagent-sh/fastagent",
3
- "version": "0.24.3",
3
+ "version": "0.24.4",
4
4
  "description": "Vibe first. Then FastAgent: turn a local agent directory into a live service in your app, on Telegram, Slack, Feishu, or behind any channel.",
5
5
  "keywords": [
6
6
  "agent",