symposium 3.0.2 → 3.0.3

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 (3) hide show
  1. package/Agent.js +1 -0
  2. package/CLAUDE.md +2 -1
  3. package/package.json +1 -1
package/Agent.js CHANGED
@@ -576,6 +576,7 @@ ${context_string}
576
576
  output_yielded = false;
577
577
  continue;
578
578
  }
579
+ yield {type: 'turn_end', thread};
579
580
  if (streaming) {
580
581
  const more = await this._awaitNextStreamingInput(thread);
581
582
  if (!more)
package/CLAUDE.md CHANGED
@@ -26,7 +26,7 @@ The execution core. After Phase 6, `agent.message()` is a non-generator dispatch
26
26
 
27
27
  `message(content, thread)` accepts three input shapes (Phase 3): a plain `string`, a `ContentBlock[]`, or an `AsyncIterable<string | ContentBlock | ContentBlock[] | ControlMessage>`. The first two behave as they always have — one user turn, one model loop, done. An async iterable enables streaming input: the agent drains the iterable into the initial user message (stopping on a `{type:'submit'}` control message, on iterable close, or once at least one content piece has arrived and the next read would block), kicks off the loop, then keeps reading concurrently. New content items pushed during a turn are queued and inserted as a user message at the next inter-turn boundary. A `{type:'cancel'}` control message terminates the loop gracefully after the in-flight turn. `{type:'auth', id, decision}` control messages carry tool-authorization responses — see the tool-authorization paragraph below. Use `createInputChannel()` (exported from `index.js`) for a simple promise-queue-backed `AsyncIterable` with `send(item)` / `close()` methods; under the hood it's implemented in `InputChannel.js`. For streaming input, the chat agent does NOT terminate after a no-tool-call turn — it waits for the next message; the run ends only when the iterable closes (or cancel is received).
28
28
 
29
- - `chat` — yields the full event set (`start`, `chunk`, `output`, `reasoning`, `tool`, `tool_response`, `tools_auth`, `retry`, `end`). If `response_schema` is set, the final assistant message is parsed against it and a `{type:'result', value}` event is yielded before `end`; the run terminates after the schema-conforming answer (no further turns).
29
+ - `chat` — yields the full event set (`start`, `chunk`, `output`, `reasoning`, `tool`, `tool_response`, `tools_auth`, `retry`, `turn_end`, `end`). `turn_end` fires after each completed turn (after the LLM finishes a response with no more tool calls), regardless of streaming vs single-shot mode — consumers use it to detect turn boundaries within a streaming run, where `start`/`end` only bracket the whole run. If `response_schema` is set, the final assistant message is parsed against it and a `{type:'result', value}` event is yielded before `end`; the run terminates after the schema-conforming answer (no further turns).
30
30
  - `utility` — `await agent.message(...)` resolves to the parsed value. With no `response_schema`, the value is the raw assistant text. With `response_schema` set, the value is the parsed JSON object: structured-output-capable models with ≤100 properties use `response_format: json_schema`; otherwise the agent falls back to a forced tool call (synthetic name `'response'`) and parses its arguments. See `convertFunctionToResponseFormat()` for the OpenAI-specific schema constraints (all properties forced to required, `additionalProperties: false`). The legacy `agent.utility = {type, function, parameters}` shape was removed in Phase 6 — use `response_schema` (a raw JSON schema) instead. `response_schema` is independent of `type` and works on chat agents too.
31
31
 
32
32
  The `execute()` loop is a `while (true)` inside an async generator with a `max_retries` (default 5) safety net wrapped around the entire turn: generate completion (forwarding `text_delta` deltas as `{type:'chunk'}` events and flipping a per-turn `output_yielded` flag) → `afterExecute` hook → yield reasoning → `handleCompletion` → if the assistant called tools, run them via `callTools` and loop; otherwise return. On error, the loop retries up to `max_retries` times per turn (hybrid strategy, Phase 5): silent if no chunk has been yielded yet, otherwise it emits `{type:'retry', attempt, reason}` so the consumer knows. A 1-second backoff is preserved for transport-level 5xx errors. Tool-execution errors are NOT retried — they're caught in `callTool()` and surfaced as `{type:'tool_response', success:false, error}`. Errors throw out of the generator naturally — there is no `error` event. Subclasses customize via `doInitThread`, `getDefaultState`, `beforeExecute(thread)`, `afterExecute(thread, completion)`, `afterHandle(thread, completion, value?)` (note: hooks no longer receive an emitter; the parameter was dropped in v3 Phase 2).
@@ -78,6 +78,7 @@ Two distinct concepts share the word "context":
78
78
  | `{type:'tools_auth', id, tools}` | uuid + pending tool calls | When `tool.authorize()` returns false; resume by sending `{type:'auth', id, decision}` on the input channel |
79
79
  | `{type:'retry', attempt, reason}` | 1-indexed retry number + error message | Yielded only when an error occurs AFTER at least one `chunk` has been streamed for the current turn (hybrid retry, Phase 5). Errors before any output are retried silently. |
80
80
  | `{type:'result', value}` | parsed value | Utility agents only |
81
+ | `{type:'turn_end', thread}` | thread object | Yielded after a turn completes (the LLM finished a response with no more tool calls). Fires once per turn whether the run continues (streaming, awaiting next user message) or ends. Use this — not `end` — to detect per-turn boundaries in a streaming session. |
81
82
  | `{type:'end', thread}` | thread object | Always yielded, even on throw (yielded from a `finally`) |
82
83
 
83
84
  Errors throw out of the generator. There is no `error` event anymore.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "symposium",
4
- "version": "3.0.2",
4
+ "version": "3.0.3",
5
5
  "description": "Agents",
6
6
  "main": "index.js",
7
7
  "author": "Domenico Giambra",