Skip to content

Commit abe3e19

Browse files
Simplify factory docs so they do not encode transient facts
- Describe ctx.session by what it omits, and point at the extensions_manage guide - Drop the hardcoded active-run limit, which will become a setting - Drop the listRuns paging parenthetical Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd
1 parent c69fcf1 commit abe3e19

3 files changed

Lines changed: 17 additions & 20 deletions

File tree

nodejs/docs/factories.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ The `run()` context provides:
6161
- `ctx.step(key, producer, options?)`: Journals the producer's JSON result under a stable key so a resume replays it without re-running the producer. A journaled (default) producer must return a JSON-serializable value; `undefined` or a non-JSON value is rejected. Pass `{ volatile: true }` to bypass the journal and run the producer every time.
6262

6363
The key is the *sole* identity: neither the producer body nor its inputs contribute to it. A resume replays the cached value for a matching key even if the producer has since changed, so version the key (`"scan-v2"`) whenever its inputs or meaning change. Journaled producers are best-effort at-least-once and may run again across crashes or concurrent same-key callers, so keep side effects idempotent.
64-
- `ctx.session`: The full session returned by `joinSession`. It remains the full session, but `factory.run` and `factory.resume` are refused while the factory body runs on the same call path.
64+
- `ctx.session`: The session returned by `joinSession`, without the APIs that start and resume factory runs. Call `extensions_manage` with `operation: "guide"` to read more about the session APIs.
6565
- `ctx.signal`: Cooperative cancellation signal for extension work and subprocesses.
6666
- `ctx.factory(...)`: Always rejects because nested factories are not supported.
6767

@@ -156,7 +156,7 @@ session.factory.resume(
156156
): Promise<FactoryRunResult>;
157157
```
158158

159-
Both resolve with the run envelope (`FactoryRunResult`) for **every** outcome — `completed`, `error`, `halted`, and `cancelled` alike. Inspect `status` and read `result` only when the run completed; a limit breach carries a typed `failure`. SDK-initiated `run` and `resume` do not request permission, so they have no declined outcome. The model's `run_factory` tool requests permission before the durable row exists; declining it creates no run row. An SDK-initiated run is refused only when four top-level runs are already active. Pre-execution resume failures throw `FactoryResumeError`, whose `code` is one of `not_found`, `non_resumable`, `already_active`, `factory_already_running`, `factory_limits_invalid`, `factory_session_disposed`, `factory_storage_unavailable`, or `factory_storage_corrupt`.
159+
Both resolve with the run envelope (`FactoryRunResult`) for **every** outcome — `completed`, `error`, `halted`, and `cancelled` alike. Inspect `status` and read `result` only when the run completed; a limit breach carries a typed `failure`. SDK-initiated `run` and `resume` do not request permission, so they have no declined outcome. The model's `run_factory` tool requests permission before the durable row exists; declining it creates no run row. An SDK-initiated run is refused only when the session already has its maximum number of active top-level runs. Pre-execution resume failures throw `FactoryResumeError`, whose `code` is one of `not_found`, `non_resumable`, `already_active`, `factory_already_running`, `factory_limits_invalid`, `factory_session_disposed`, `factory_storage_unavailable`, or `factory_storage_corrupt`.
160160

161161
An agent that no longer has a prior run's ID in context can recover it with `factories_manage` and `operation: "runs"`, which lists the session's factory runs with their IDs and statuses. This matters for resume: a run that reached a limit keeps its journal, so resuming it replays completed work for free, while restarting it from scratch pays for that work twice.
162162

@@ -210,7 +210,7 @@ const page = await session.factory.getRunProgress(runId, {
210210
});
211211
```
212212
213-
- `listRuns()` returns the newest default page (the SDK sends `{}`, so the runtime defaults to 200 runs and caps the page at 500).
213+
- `listRuns()` returns the newest default page of this session's durable factory runs.
214214
- `getRunDetail(runId)` returns phases, prompt-safe agent summaries, and the latest progress page.
215215
- `getRunProgress(runId, options?)` pages progress forward, backward, by phase, or from the latest tail.
216216

nodejs/src/factory.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,8 @@ export interface FactoryContext<TArgs extends JsonValue = JsonValue> {
175175
/** Caller-supplied input, forwarded verbatim. */
176176
args: TArgs;
177177
/**
178-
* The same full session instance returned by `joinSession`.
179-
*
180-
* While the factory body runs, `factory.run` and `factory.resume` are
181-
* refused on the same call path.
178+
* The session instance returned by `joinSession`, without the APIs that
179+
* start and resume factory runs.
182180
*/
183181
session: CopilotSession;
184182
/** Cooperative cancellation signal for the current factory run. */
@@ -291,7 +289,8 @@ export interface SessionFactoryApi {
291289
* declined outcome. The model's `run_factory` tool requests permission
292290
* before a durable row exists; declining it creates no run row. Failures
293291
* that occur before a run exists (such as an unknown factory or attempting
294-
* to start a fifth top-level run while four are active) still reject.
292+
* to start a run while the session is at its active top-level run limit)
293+
* still reject.
295294
*/
296295
run(name: string, options?: RunOptions): Promise<FactoryRunResult>;
297296
run<TArgs extends JsonValue>(
@@ -324,8 +323,7 @@ export interface SessionFactoryApi {
324323
*/
325324
waitForRun(runId: string, options?: { signal?: AbortSignal }): Promise<FactoryRunResult>;
326325
/**
327-
* List the newest default page (the SDK sends `{}`, so the runtime defaults
328-
* to 200 runs and caps the page at 500).
326+
* List the newest default page of this session's durable factory runs.
329327
*/
330328
listRuns(): Promise<FactoryRunSummary[]>;
331329
/** Read durable phases, direct agents, and the latest progress tail for a run. */

nodejs/test/factory.test.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,7 @@ describe("factories", () => {
405405
it("documents factory invocation and list paging behavior accurately", () => {
406406
const guide = readFileSync(new URL("../docs/factories.md", import.meta.url), "utf8");
407407
const publicApi = readFileSync(new URL("../src/factory.ts", import.meta.url), "utf8");
408-
const listRunsPagingWording =
409-
"newest default page (the SDK sends `{}`, so the runtime defaults to 200 runs and caps the page at 500)";
408+
const listRunsPagingWording = "newest default page of this session's durable factory runs";
410409
const resumeCodes = [
411410
"not_found",
412411
"non_resumable",
@@ -439,26 +438,26 @@ describe("factories", () => {
439438
"`run_factory` tool requests permission before the durable row exists"
440439
);
441440
expect(normalizedGuide).toContain("declining it creates no run row");
442-
expect(normalizedGuide).toContain("only when four top-level runs are already active");
441+
expect(normalizedGuide).toContain("its maximum number of active top-level runs");
443442
for (const code of resumeCodes) {
444443
expect(guide).toContain(`\`${code}\``);
445444
}
446445
expect(guide).toContain(
447446
"Options are exactly `label`, `schema`, `model`, `agent`, `reasoningEffort`, and `contextTier`"
448447
);
449-
expect(normalizedGuide).toContain("full session returned by `joinSession`");
450-
expect(normalizedGuide).toContain("factory body runs on the same call path");
448+
expect(normalizedGuide).toContain(
449+
"session returned by `joinSession`, without the APIs that start and resume factory runs"
450+
);
451451

452452
expect(normalizedPublicApi).toContain("SDK-initiated runs do not request permission");
453453
expect(normalizedPublicApi).toContain("declining it creates no run row");
454-
expect(normalizedPublicApi).toContain("fifth top-level run while four are active");
455-
expect(normalizedPublicApi).toContain("SDK-initiated resumes do not request permission");
456-
expect(normalizedPublicApi).toContain("with a documented resume code rejects with");
457454
expect(normalizedPublicApi).toContain(
458-
"same full session instance returned by `joinSession`"
455+
"while the session is at its active top-level run limit"
459456
);
457+
expect(normalizedPublicApi).toContain("SDK-initiated resumes do not request permission");
458+
expect(normalizedPublicApi).toContain("with a documented resume code rejects with");
460459
expect(normalizedPublicApi).toContain(
461-
"factory.run` and `factory.resume` are refused on the same call path"
460+
"session instance returned by `joinSession`, without the APIs that start and resume factory runs"
462461
);
463462
});
464463

0 commit comments

Comments
 (0)