doer-agent 0.9.11 → 0.9.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.
@@ -6,6 +6,50 @@ const MAX_ITEM_TEXT_CHARS = 4_000;
6
6
  const MAX_TURN_TEXT_CHARS = 8_000;
7
7
  const MAX_TRANSCRIPT_CHARS = 160_000;
8
8
  const MAX_HANDOFF_CHARS = 20_000;
9
+ const HANDOFF_USER_MARKER = "Continue from the handoff summary above and wait for my next request.";
10
+ export function buildThreadHandoffInjectionItems(handoff) {
11
+ return [
12
+ {
13
+ type: "message",
14
+ role: "assistant",
15
+ content: [{
16
+ type: "output_text",
17
+ text: handoff,
18
+ }],
19
+ },
20
+ ];
21
+ }
22
+ export function buildThreadHandoffSeedInput() {
23
+ return [{
24
+ type: "text",
25
+ text: HANDOFF_USER_MARKER,
26
+ }];
27
+ }
28
+ async function waitForThreadTurnStarted(manager, threadId, timeoutMs = 1_000) {
29
+ await new Promise((resolve) => {
30
+ let settled = false;
31
+ let cleanup = () => { };
32
+ const finish = () => {
33
+ if (settled) {
34
+ return;
35
+ }
36
+ settled = true;
37
+ clearTimeout(timer);
38
+ cleanup();
39
+ resolve();
40
+ };
41
+ const timer = setTimeout(finish, timeoutMs);
42
+ cleanup = manager.onNotification((method, params) => {
43
+ if (method !== "turn/started") {
44
+ return;
45
+ }
46
+ const event = recordValue(params);
47
+ if (stringValue(event?.threadId) === threadId) {
48
+ finish();
49
+ }
50
+ });
51
+ });
52
+ }
9
53
  function recordValue(value) {
10
54
  return value && typeof value === "object" && !Array.isArray(value)
11
55
  ? value
@@ -339,16 +383,26 @@ export async function createCodexThreadHandoff(args) {
339
383
  }
340
384
  try {
341
385
  args.onProgress?.("injecting");
386
+ // Raw injected items do not make a new thread discoverable through thread/list.
387
+ const seedStarted = waitForThreadTurnStarted(args.manager, threadId);
388
+ const seedResult = recordValue(await args.manager.request("turn/start", {
389
+ threadId,
390
+ input: buildThreadHandoffSeedInput(),
391
+ }, 30_000));
392
+ const seedTurnId = stringValue(recordValue(seedResult?.turn)?.id);
393
+ if (!seedTurnId) {
394
+ throw new Error("Codex app-server did not return a handoff seed turn");
395
+ }
396
+ await seedStarted;
397
+ await args.manager.request("turn/interrupt", {
398
+ threadId,
399
+ turnId: seedTurnId,
400
+ }, 30_000).catch((error) => {
401
+ warnings.push(`Could not interrupt handoff seed turn: ${error instanceof Error ? error.message : String(error)}`);
402
+ });
342
403
  await args.manager.request("thread/inject_items", {
343
404
  threadId,
344
- items: [{
345
- type: "message",
346
- role: "assistant",
347
- content: [{
348
- type: "output_text",
349
- text: handoff,
350
- }],
351
- }],
405
+ items: buildThreadHandoffInjectionItems(handoff),
352
406
  }, 90_000);
353
407
  }
354
408
  catch (error) {
@@ -1,6 +1,24 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
- import { buildBoundedHandoffTranscript, summarizeThreadTurn, } from "./codex-thread-handoff.js";
3
+ import { buildBoundedHandoffTranscript, buildThreadHandoffInjectionItems, buildThreadHandoffSeedInput, summarizeThreadTurn, } from "./codex-thread-handoff.js";
4
+ test("buildThreadHandoffInjectionItems adds the summary to model-visible history", () => {
5
+ assert.deepEqual(buildThreadHandoffInjectionItems("# Thread handoff\n\nContinue the work."), [
6
+ {
7
+ type: "message",
8
+ role: "assistant",
9
+ content: [{
10
+ type: "output_text",
11
+ text: "# Thread handoff\n\nContinue the work.",
12
+ }],
13
+ },
14
+ ]);
15
+ });
16
+ test("buildThreadHandoffSeedInput creates a real user turn for thread indexing", () => {
17
+ assert.deepEqual(buildThreadHandoffSeedInput(), [{
18
+ type: "text",
19
+ text: "Continue from the handoff summary above and wait for my next request.",
20
+ }]);
21
+ });
4
22
  test("summarizeThreadTurn omits raw command output, diffs, images, and MCP results", () => {
5
23
  const summary = summarizeThreadTurn({
6
24
  id: "turn-1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doer-agent",
3
- "version": "0.9.11",
3
+ "version": "0.9.12",
4
4
  "description": "Reverse-polling agent runtime for doer",
5
5
  "type": "module",
6
6
  "main": "dist/agent.js",