@with-logic/elwood 0.1.1 → 0.1.2
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/CHANGELOG.md +17 -3
- package/CONTRIBUTING.md +104 -0
- package/README.md +23 -17
- package/SECURITY.md +42 -0
- package/dist/claude/model-picker.d.ts.map +1 -1
- package/dist/claude/model-picker.js +4 -3
- package/dist/claude/model-picker.js.map +1 -1
- package/dist/claude/model-switch-confirmation.js +9 -1
- package/dist/claude/model-switch-confirmation.js.map +1 -1
- package/dist/claude/screen-table.js +1 -1
- package/dist/claude/screen-table.js.map +1 -1
- package/dist/claude/session/build.d.ts.map +1 -1
- package/dist/claude/session/build.js +3 -0
- package/dist/claude/session/build.js.map +1 -1
- package/dist/claude/session/start-or-resume.js +1 -0
- package/dist/claude/session/start-or-resume.js.map +1 -1
- package/dist/cli/interactive/spawn.d.ts.map +1 -1
- package/dist/cli/interactive/spawn.js +30 -9
- package/dist/cli/interactive/spawn.js.map +1 -1
- package/dist/cli/models/index.d.ts.map +1 -1
- package/dist/cli/models/index.js +6 -3
- package/dist/cli/models/index.js.map +1 -1
- package/dist/cli/request/layers.d.ts +2 -0
- package/dist/cli/request/layers.d.ts.map +1 -1
- package/dist/cli/request/layers.js +9 -5
- package/dist/cli/request/layers.js.map +1 -1
- package/dist/cli/sessions/index.d.ts +9 -2
- package/dist/cli/sessions/index.d.ts.map +1 -1
- package/dist/cli/sessions/index.js +2 -6
- package/dist/cli/sessions/index.js.map +1 -1
- package/dist/cli/sessions/settings.d.ts +11 -0
- package/dist/cli/sessions/settings.d.ts.map +1 -0
- package/dist/cli/sessions/settings.js +26 -0
- package/dist/cli/sessions/settings.js.map +1 -0
- package/dist/codex/model-picker.d.ts.map +1 -1
- package/dist/codex/model-picker.js +3 -2
- package/dist/codex/model-picker.js.map +1 -1
- package/dist/codex/session/build.d.ts.map +1 -1
- package/dist/codex/session/build.js +5 -7
- package/dist/codex/session/build.js.map +1 -1
- package/dist/codex/session/start-or-resume.js +1 -0
- package/dist/codex/session/start-or-resume.js.map +1 -1
- package/dist/core/input/index.d.ts.map +1 -1
- package/dist/core/input/index.js +15 -3
- package/dist/core/input/index.js.map +1 -1
- package/dist/core/models/input.d.ts +3 -0
- package/dist/core/models/input.d.ts.map +1 -0
- package/dist/core/models/input.js +9 -0
- package/dist/core/models/input.js.map +1 -0
- package/dist/core/models/picker.d.ts +2 -1
- package/dist/core/models/picker.d.ts.map +1 -1
- package/dist/core/models/picker.js +6 -5
- package/dist/core/models/picker.js.map +1 -1
- package/dist/core/models/tui-screen.d.ts +1 -1
- package/dist/core/models/tui-screen.d.ts.map +1 -1
- package/dist/core/models/tui-screen.js +26 -3
- package/dist/core/models/tui-screen.js.map +1 -1
- package/dist/runtime/session/base.d.ts.map +1 -1
- package/dist/runtime/session/base.js +4 -1
- package/dist/runtime/session/base.js.map +1 -1
- package/dist/runtime/session/commands.d.ts +2 -1
- package/dist/runtime/session/commands.d.ts.map +1 -1
- package/dist/runtime/session/commands.js +10 -4
- package/dist/runtime/session/commands.js.map +1 -1
- package/dist/runtime/session/lifecycle.d.ts +3 -4
- package/dist/runtime/session/lifecycle.d.ts.map +1 -1
- package/dist/runtime/session/lifecycle.js +6 -5
- package/dist/runtime/session/lifecycle.js.map +1 -1
- package/dist/state/directories.d.ts +4 -0
- package/dist/state/directories.d.ts.map +1 -0
- package/dist/state/directories.js +41 -0
- package/dist/state/directories.js.map +1 -0
- package/dist/state/files.d.ts.map +1 -1
- package/dist/state/files.js +25 -11
- package/dist/state/files.js.map +1 -1
- package/dist/state/private-session.d.ts.map +1 -1
- package/dist/state/private-session.js +2 -0
- package/dist/state/private-session.js.map +1 -1
- package/dist/state/store.d.ts.map +1 -1
- package/dist/state/store.js +7 -1
- package/dist/state/store.js.map +1 -1
- package/docs/cli-behavior.md +1 -1
- package/docs/codex/cli.md +57 -0
- package/docs/codex/config.md +47 -0
- package/docs/codex/hooks.md +70 -0
- package/examples/README.md +56 -0
- package/examples/full.ts +113 -0
- package/examples/minimal.ts +15 -0
- package/examples/stream.ts +39 -0
- package/examples/support.ts +71 -0
- package/package.json +10 -5
- package/prd/01-overview.md +21 -0
- package/prd/02-goals.md +47 -0
- package/prd/03-terminology.md +32 -0
- package/prd/04-architecture.md +121 -0
- package/prd/05-api/5.1-starting-claude.md +108 -0
- package/prd/05-api/5.2-resuming-claude.md +86 -0
- package/prd/05-api/5.3-claude-session-api.md +658 -0
- package/prd/05-api/5.4-events.md +145 -0
- package/prd/05-api/5.5-starting-codex.md +106 -0
- package/prd/05-api/5.6-resuming-codex.md +48 -0
- package/prd/05-api/5.7-codex-session-api.md +425 -0
- package/prd/05-api/5.8-session-api.md +122 -0
- package/prd/05-api/5.9-loops.md +170 -0
- package/prd/05-api/README.md +19 -0
- package/prd/06-claude-hook-bridge.md +164 -0
- package/prd/07-claude-policy.md +15 -0
- package/prd/07a-codex-hook-bridge.md +76 -0
- package/prd/08-state.md +127 -0
- package/prd/09-lifecycle.md +222 -0
- package/prd/10-errors.md +55 -0
- package/prd/11-test-app.md +53 -0
- package/prd/12-e2e.md +23 -0
- package/prd/12a-cli.md +375 -0
- package/prd/13-latitude.md +12 -0
- package/prd/14-conformance.md +333 -0
- package/prd/15-open-notes.md +8 -0
- package/prd/README.md +45 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
## 5.4 Events
|
|
2
|
+
|
|
3
|
+
The primary event API is typed event-emitter style.
|
|
4
|
+
|
|
5
|
+
Required event families:
|
|
6
|
+
|
|
7
|
+
- `terminal:data`: raw PTY output bytes/string for live rendering.
|
|
8
|
+
- `terminal:exit`: PTY process exit.
|
|
9
|
+
|
|
10
|
+
`resize` should update both terminal models while the process is alive. If the
|
|
11
|
+
native PTY reports that its file descriptor is already closed during a resize
|
|
12
|
+
race with process exit, Elwood should treat that resize as a no-op and rely on
|
|
13
|
+
the normal exit event to update lifecycle status. Other native PTY resize errors
|
|
14
|
+
must still surface.
|
|
15
|
+
- `status`: session lifecycle/status changes.
|
|
16
|
+
- `warning`: typed non-fatal adapter or environment issues observed during the
|
|
17
|
+
session, such as Codex MCP startup warnings.
|
|
18
|
+
- `loop`: adapter-neutral recurring-loop lifecycle and failure events (§5.9).
|
|
19
|
+
- `activity`: adapter-neutral live events for common observability, including
|
|
20
|
+
lifecycle changes, user messages, assistant messages, reasoning, tool calls,
|
|
21
|
+
tool results, web search, notifications, warnings, startup prompt automation,
|
|
22
|
+
attention (blocked-on-a-human-decision) transitions, generic hooks, hook
|
|
23
|
+
results, and hook errors.
|
|
24
|
+
- `hook`: every Claude hook event after parsing and validation.
|
|
25
|
+
- `codex:transcript`: live, best-effort Codex transcript observations for
|
|
26
|
+
activity that Codex does not expose as hook events.
|
|
27
|
+
- `hookError`: hook bridge or parent-handler errors that failed open.
|
|
28
|
+
|
|
29
|
+
`activity` is the preferred event for parent apps that want a simple unified
|
|
30
|
+
"send message, observe what happened, send another message" loop across
|
|
31
|
+
adapters. Every activity event includes:
|
|
32
|
+
|
|
33
|
+
- `elwoodSessionId`
|
|
34
|
+
- `agent`: `claude` or `codex`
|
|
35
|
+
- `source`: `hook`, `transcript`, `terminal`, or `lifecycle`
|
|
36
|
+
- `kind`: normalized event kind
|
|
37
|
+
- `label`: short display label
|
|
38
|
+
- optional `text`
|
|
39
|
+
- optional normalized metadata fields:
|
|
40
|
+
- `hookEventName`
|
|
41
|
+
- `turnId`
|
|
42
|
+
- `toolName`
|
|
43
|
+
- `toolUseId`
|
|
44
|
+
- `toolInput`: the tool call's input arguments, serialized to a string
|
|
45
|
+
(already-string inputs pass through; structured inputs are JSON-stringified),
|
|
46
|
+
present on `tool_call` activity when the source exposes it.
|
|
47
|
+
- `toolOutput`: the tool result's output, serialized to a string with the same
|
|
48
|
+
rules, present on `tool_result` activity when the source exposes it.
|
|
49
|
+
- `status`
|
|
50
|
+
- `exitCode`
|
|
51
|
+
- `failedOpen`
|
|
52
|
+
- `transcriptPath`
|
|
53
|
+
- optional `raw` in-memory source payload
|
|
54
|
+
|
|
55
|
+
`raw` is live-only but intentionally not redacted. Parent applications that
|
|
56
|
+
subscribe to activity events should treat `raw` as potentially containing user
|
|
57
|
+
prompts, tool inputs, tool outputs, and hook payload details.
|
|
58
|
+
|
|
59
|
+
Parent apps should not need `raw` for common timeline rendering. Elwood MUST
|
|
60
|
+
populate the normalized metadata fields whenever those values are present in the
|
|
61
|
+
source hook, transcript item, or lifecycle event. Adapter-specific `raw` remains
|
|
62
|
+
available for deep debugging and unsupported future payloads.
|
|
63
|
+
|
|
64
|
+
When Elwood detects and answers an interactive startup prompt on behalf of the
|
|
65
|
+
parent app, it MUST emit an activity event with `source: "terminal"`,
|
|
66
|
+
`kind: "startup_prompt"`, a stable label, and text describing the key sent. This
|
|
67
|
+
`startup_prompt` activity is emitted only after the prompt's PTY write actually
|
|
68
|
+
fulfills; a rejected write emits no such activity and instead surfaces the
|
|
69
|
+
`startup_prompt_write_failed` warning (§5.7) while leaving the prompt retryable.
|
|
70
|
+
The stable startup-prompt label set is EXACTLY the following, and no other value
|
|
71
|
+
may be emitted with `kind: "startup_prompt"`:
|
|
72
|
+
|
|
73
|
+
| label | agent | defined by |
|
|
74
|
+
| ----------------- | -------------- | --------------------------- |
|
|
75
|
+
| `workspace_trust` | claude, codex | C-CLAUDE-10, C-CODEX-11/15 |
|
|
76
|
+
| `skill_trust` | claude | C-CLAUDE-14 |
|
|
77
|
+
| `plugin_trust` | claude | C-CLAUDE-14 |
|
|
78
|
+
| `mcp_trust` | claude | C-CLAUDE-14 |
|
|
79
|
+
| `bypass_permissions` | claude | C-CLAUDE-21 |
|
|
80
|
+
| `browser_tools` | claude | C-CLAUDE-11 |
|
|
81
|
+
| `hook_trust` | codex | C-CODEX-06/15 |
|
|
82
|
+
| `update` | codex | C-CODEX-12 |
|
|
83
|
+
|
|
84
|
+
The trust-prompt labels (`workspace_trust`, `skill_trust`, `plugin_trust`,
|
|
85
|
+
`mcp_trust`, `bypass_permissions`, `hook_trust`) are exactly the ids in the
|
|
86
|
+
trust-prompt allowlist; a second implementation MUST treat this table as the
|
|
87
|
+
complete output union.
|
|
88
|
+
|
|
89
|
+
Under a caller's full-trust launch (`autotrust`), an agent MUST NEVER be left
|
|
90
|
+
waiting on a trust gate: when an allowlisted trust prompt is visible in the
|
|
91
|
+
current rendered frame, Elwood selects its affirmative option and sends it —
|
|
92
|
+
always say yes. The trust prompt family is folder/directory trust, the CLI's
|
|
93
|
+
first-run prompts for loading a skill, a plugin, or an MCP server, and Claude's
|
|
94
|
+
one-time bypass-permissions acceptance dialog (§5.1). This is an
|
|
95
|
+
EXPLICIT ALLOWLIST: each entry names one prompt by its on-screen HEADER wording
|
|
96
|
+
and an affirmative-option matcher. Recognition — and ONLY recognition — is the
|
|
97
|
+
guard: (1) Elwood auto-answers only ALLOWLISTED prompts, so an off-allowlist
|
|
98
|
+
first-run confirmation is left to the human and a future CLI security gate is
|
|
99
|
+
never blanket-bypassed; and (2) a prompt is recognized ONLY by its HEADER wording
|
|
100
|
+
outside the option region — a trust phrase appearing only inside a numbered or
|
|
101
|
+
cursor-selectable option label (e.g. "1. Yes, trust this plugin and grant admin
|
|
102
|
+
access") does NOT identify a prompt, so a hostile option cannot spoof one.
|
|
103
|
+
Recognition matches the header against the frame's joined non-option lines, so a
|
|
104
|
+
header wrapped across physical rows still matches; the affirmative is the first
|
|
105
|
+
option whose label matches the prompt's affirmative pattern. Numbered layouts
|
|
106
|
+
are answered by sending their option number; cursor layouts are answered by
|
|
107
|
+
navigating from the rendered selection to the affirmative and pressing Enter.
|
|
108
|
+
Elwood matches only against the CURRENT frame, never accumulated history. If an
|
|
109
|
+
allowlisted prompt is recognized but its affirmative option has not rendered
|
|
110
|
+
yet, Elwood emits an `attention` activity (labelled with the prompt id) at most
|
|
111
|
+
once, and keeps watching so a later frame carrying the option is still answered —
|
|
112
|
+
it never permanently wedges. These trust prompts are answered only when
|
|
113
|
+
`autotrust` is set; with it off, an unanswered trust prompt is a blocking prompt
|
|
114
|
+
that holds session state for the human. A prompt that Elwood
|
|
115
|
+
auto-answers (including an always-answered one) MUST NOT be classified as a
|
|
116
|
+
blocking prompt.
|
|
117
|
+
|
|
118
|
+
Two prompts are exceptions to the `autotrust` gate. Codex hook trust
|
|
119
|
+
(`hook_trust`) is Elwood's OWN integration — the session cannot function without
|
|
120
|
+
its hook bridge trusted — so it is always answered, and `update` is not a trust
|
|
121
|
+
decision at all; both are handled independently of `autotrust`.
|
|
122
|
+
Every hook dispatch must also emit an adapter-neutral hook-result activity after
|
|
123
|
+
parent handlers run. The result activity must include the hook event name,
|
|
124
|
+
whether the dispatch failed open, and the raw in-memory result payload when
|
|
125
|
+
present. Missing parent handlers are normal no-decision responses and must not
|
|
126
|
+
be labeled as failed-open errors. Handler timeouts, invalid responses, thrown
|
|
127
|
+
handlers, and bridge errors must emit both `hookError` and an adapter-neutral `activity` with
|
|
128
|
+
`kind: "hook_error"`.
|
|
129
|
+
|
|
130
|
+
`HookErrorEvent.category` values are `timeout`, `handler_error`,
|
|
131
|
+
`invalid_input`, `invalid_response`, and `bridge_error`.
|
|
132
|
+
|
|
133
|
+
Hook-specific events remain the source of truth for event-specific decisions,
|
|
134
|
+
typed control responses, and adapter-specific payloads.
|
|
135
|
+
|
|
136
|
+
Elwood may provide convenience registration by hook name:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
session.on("hook:PreToolUse", async (event) => {
|
|
140
|
+
return { permissionDecision: "deny", permissionDecisionReason: "No writes" };
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The exact naming may vary, but the type system MUST narrow event payload and
|
|
145
|
+
valid response shape by hook event name.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
## 5.5 Starting Codex
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
type StartCodexOptions = {
|
|
5
|
+
readonly cwd: string;
|
|
6
|
+
readonly stateDir?: string;
|
|
7
|
+
readonly initialSize?: TerminalSize;
|
|
8
|
+
readonly hooks?: CodexHookHandlers;
|
|
9
|
+
readonly persona?: string;
|
|
10
|
+
readonly model?: string;
|
|
11
|
+
readonly reasoningEffort?: CodexReasoningEffort;
|
|
12
|
+
readonly profile?: string;
|
|
13
|
+
readonly sandbox?: "read-only" | "workspace-write" | "danger-full-access";
|
|
14
|
+
readonly approvalPolicy?: "untrusted" | "on-request" | "never";
|
|
15
|
+
readonly configOverrides?: readonly string[];
|
|
16
|
+
readonly highTrust?: boolean;
|
|
17
|
+
readonly autoupdate?: boolean;
|
|
18
|
+
readonly autotrust?: boolean;
|
|
19
|
+
readonly hookTimeoutMs?: number;
|
|
20
|
+
readonly strictVersionCheck?: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type CodexReasoningEffort =
|
|
24
|
+
| "none"
|
|
25
|
+
| "minimal"
|
|
26
|
+
| "low"
|
|
27
|
+
| "medium"
|
|
28
|
+
| "high"
|
|
29
|
+
| "xhigh"
|
|
30
|
+
| "max";
|
|
31
|
+
|
|
32
|
+
declare function startCodex(options: StartCodexOptions): Promise<CodexSessionApi>;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Elwood sets the PTY working directory and also passes Codex `--cd <cwd>` so
|
|
36
|
+
Codex's own session state and the shell's current directory agree.
|
|
37
|
+
|
|
38
|
+
`reasoningEffort`, when set, is forwarded as the Codex `-c
|
|
39
|
+
model_reasoning_effort=<value>` config override so the session starts at the
|
|
40
|
+
requested reasoning effort. Codex validates this value SERVER-SIDE — an
|
|
41
|
+
out-of-enum value launches successfully and then fails at the first turn with a
|
|
42
|
+
provider error — so Elwood validates it against the `CodexReasoningEffort` set
|
|
43
|
+
BEFORE spawning and rejects an out-of-enum value with the typed
|
|
44
|
+
`codex_invalid_reasoning_effort` error (listing the valid values), turning a
|
|
45
|
+
deferred mid-session failure into a fast, clear start-time rejection. Reasoning
|
|
46
|
+
effort and model are independent axes programmatically (effort is its own `-c`
|
|
47
|
+
key); Elwood emits the reserved `model_reasoning_effort` override AFTER caller
|
|
48
|
+
`configOverrides`, so `reasoningEffort` wins over a hand-rolled duplicate. The
|
|
49
|
+
effort applies to the launched session only and is NOT persisted by Elwood (the
|
|
50
|
+
config-restore teardown already strips a root `model_reasoning_effort`, §5.3); a
|
|
51
|
+
resumed session must re-supply it (§5.6), consistent with `model`.
|
|
52
|
+
|
|
53
|
+
`configOverrides` contains raw Codex `key=value` overrides passed through
|
|
54
|
+
`--config` after Elwood's generated hook command config so callers can
|
|
55
|
+
intentionally override non-reserved Codex behavior. Callers own TOML correctness
|
|
56
|
+
for these raw overrides; Elwood shell-quotes each override but does not parse or
|
|
57
|
+
rewrite its TOML value. Elwood's generated `features.hooks=true` and
|
|
58
|
+
`hookTrust="trust-all"` overrides are reserved and applied after caller config
|
|
59
|
+
overrides because enabled, trusted hooks are required for Elwood to function.
|
|
60
|
+
When `autoupdate` is true, Elwood runs `codex update` from the user's login
|
|
61
|
+
shell before spawning Codex and rechecks the version after the update. If Codex
|
|
62
|
+
later shows an interactive update prompt inside the TUI, Elwood skips that
|
|
63
|
+
prompt through PTY input, including Codex's cursor-addressed update screen.
|
|
64
|
+
Elwood never selects "Update now" inside the live TUI (an in-TUI update restarts
|
|
65
|
+
Codex out from under the session); the real update is the preflight `codex update`
|
|
66
|
+
above, and the in-TUI prompt is always skipped. The skip is edge-triggered, so if
|
|
67
|
+
the same update screen reappears after a restart — the update did not take — Elwood
|
|
68
|
+
skips it again rather than leaving the session stuck looping on it. While an
|
|
69
|
+
update screen remains rendered, Elwood treats it as input-blocking even after the
|
|
70
|
+
skip key is written: readiness and queued persona/caller input stay suspended until
|
|
71
|
+
the screen clears. A captured first-party banner that renders before its options
|
|
72
|
+
therefore fails safe, and a following safe-option-only frame remains latched as the
|
|
73
|
+
same prompt; generic agent prose containing "update available" does not activate
|
|
74
|
+
the blocker. Because current Codex versions can paint this menu before their input
|
|
75
|
+
loop reliably accepts the first key, Elwood retries the safe numbered choice for a
|
|
76
|
+
bounded interval. Before every write it re-reads the current frame and requires the
|
|
77
|
+
still-active first-party prompt generation plus that frame's safe numbered option;
|
|
78
|
+
this includes an option-only continuation belonging to a previously captured banner.
|
|
79
|
+
It stops without another write once that generation clears or changes, and fails the
|
|
80
|
+
startup operation if the update prompt remains but its safe option disappears or the
|
|
81
|
+
retry interval is exhausted. A blocked update screen therefore becomes a bounded run
|
|
82
|
+
failure even when the caller supplied no whole-run timeout; delayed input cannot
|
|
83
|
+
reach the restored composer or a replacement dialog. This prevents a queued Enter
|
|
84
|
+
from selecting Codex's default "Update now" action (C-CODEX-12).
|
|
85
|
+
`strictVersionCheck` makes unparseable Codex versions fatal instead of
|
|
86
|
+
warning-and-continuing.
|
|
87
|
+
|
|
88
|
+
`highTrust` is the same agent-neutral "never ask for permissions" switch as on
|
|
89
|
+
Claude (§5.1). On Codex, `highTrust: true` expands to `sandbox:
|
|
90
|
+
"danger-full-access"` with `approvalPolicy: "never"` before the posture is
|
|
91
|
+
persisted or the process is spawned. Supplying an explicit `sandbox` or
|
|
92
|
+
`approvalPolicy` together with `highTrust: true` is a configuration error
|
|
93
|
+
rejected before spawn with the typed `codex_high_trust_conflict` error.
|
|
94
|
+
|
|
95
|
+
`autotrust` is an opt-in convenience for embedded/headless parent apps.
|
|
96
|
+
When true, Elwood detects Codex's first-party directory trust prompt in the
|
|
97
|
+
rendered terminal and chooses the trust/continue option through PTY input,
|
|
98
|
+
emitting `startup_prompt` activity with label `workspace_trust`. The default is
|
|
99
|
+
false because trusting a workspace is a security-sensitive decision. Independent
|
|
100
|
+
of `autotrust`, Codex's `Hooks need review` prompt is answered once — hook trust
|
|
101
|
+
is Elwood's own integration, required for the session to function, not
|
|
102
|
+
third-party trust — emitting `startup_prompt` activity with label `hook_trust`.
|
|
103
|
+
|
|
104
|
+
`persona` behaves exactly as specified for Claude in §5.1: enqueued before
|
|
105
|
+
control returns to the caller, delivered as the first user message on the first
|
|
106
|
+
ready transition, never persisted, and never re-sent on resume.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
## 5.6 Resuming Codex
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
type ResumeCodexOptions = {
|
|
5
|
+
readonly elwoodSessionId: string;
|
|
6
|
+
readonly cwd?: string;
|
|
7
|
+
readonly stateDir?: string;
|
|
8
|
+
readonly hooks?: CodexHookHandlers;
|
|
9
|
+
readonly initialSize?: TerminalSize;
|
|
10
|
+
readonly reasoningEffort?: CodexReasoningEffort;
|
|
11
|
+
readonly sandbox?: CodexSandboxMode;
|
|
12
|
+
readonly approvalPolicy?: CodexApprovalPolicy;
|
|
13
|
+
readonly highTrust?: boolean;
|
|
14
|
+
readonly autoupdate?: boolean;
|
|
15
|
+
readonly autotrust?: boolean;
|
|
16
|
+
readonly hookTimeoutMs?: number;
|
|
17
|
+
readonly strictVersionCheck?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
declare function resumeCodex(options: ResumeCodexOptions): Promise<CodexSessionApi>;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Resume uses Elwood's durable session metadata to relaunch `codex resume` with
|
|
24
|
+
Codex's internal session id when Elwood has observed it through hooks.
|
|
25
|
+
Because the default state store is project-local, callers that resume from a
|
|
26
|
+
different process working directory must pass the original `cwd` or the explicit
|
|
27
|
+
`stateDir` used at start time. Calling `resumeCodex({ elwoodSessionId })`
|
|
28
|
+
without `cwd` or `stateDir` uses the current process working directory to
|
|
29
|
+
discover the project-local state store.
|
|
30
|
+
|
|
31
|
+
`resumeCodex` must verify that the persisted Elwood session record belongs to
|
|
32
|
+
the Codex adapter before launching. Passing a Claude session ID to `resumeCodex`
|
|
33
|
+
must fail with a typed error.
|
|
34
|
+
|
|
35
|
+
If Elwood has not observed and persisted Codex's internal session id,
|
|
36
|
+
`resumeCodex` must fail explicitly. It must not silently launch a fresh Codex
|
|
37
|
+
conversation under the existing Elwood session ID. Future implementations may
|
|
38
|
+
offer an explicit caller opt-in fallback such as `codex resume --last`, but the
|
|
39
|
+
default behavior must be fail-closed.
|
|
40
|
+
|
|
41
|
+
Like the start path, `resumeCodex` accepts the launch-policy privilege options
|
|
42
|
+
`sandbox` and `approvalPolicy`. The session record persists the launch posture
|
|
43
|
+
at start and resume defaults to it, so a bare `resumeCodex` relaunches with the
|
|
44
|
+
same sandbox and approval policy the session started with; explicit resume
|
|
45
|
+
options override field by field and the effective posture is re-persisted.
|
|
46
|
+
`highTrust: true` on resume expands to the same explicit `danger-full-access` /
|
|
47
|
+
`never` override as on start and conflicts with explicit `sandbox` /
|
|
48
|
+
`approvalPolicy` the same way (`codex_high_trust_conflict`).
|