@khorsheed/dsh-local-agent-codex 0.2.0-rc.1 → 0.2.0

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.
@@ -1,30 +1,51 @@
1
1
  import { bindSettingsScope } from '@khorsheed/dsh-local-agent/src/client/settings-scope.ts';
2
- import { en, NS, zh } from './locales.js';
3
- import { CodexBundleConfig, CodexSettingsCard, } from './SettingsCard.js';
4
- export { CodexBundleConfig, CodexSettingsCard } from './SettingsCard.js';
5
- export { en, NS, zh };
6
2
  /**
7
- * This bundle's package name — the key the Plugins page dispatches
8
- * `plugins.bundle.config` entries on (identity triangle: cordis.patch.yml,
9
- * tsdown.config.ts, invariant.ts).
3
+ * Local-agent-codex plugin, browser half: the codex settings card — a section
4
+ * of the family core's「本机 Agent 委派」family card on the Plugins page
5
+ * (registered through the core's `localAgentUi.registerHarnessCard` while the
6
+ * Host serves the `local-agent-codex` namespace), or this package's own
7
+ * `plugins.item` entry when the core predates the family card (the
8
+ * declare-and-degrade fallback). Every install shape serves the row's
9
+ * volatile Config under the row id —
10
+ * standalone install, family bundle, or mode pack — so the card follows the
11
+ * served namespace rather than any carrier bundle, and a composition without
12
+ * the row shows no trace of it. The card carries the family core's shared
13
+ * ProviderAuthBlock (login/logout/device-code, backed by the core's
14
+ * localAgentGateway Remote and the commands channel — endpoint-key only, no
15
+ * login flow), the endpoint credential form, the family core's shared
16
+ * ModelListSection (the curated ordered shortlist — first entry is the
17
+ * default model — edited through the gateway's modelList/configureModelList
18
+ * Remotes, the add dialog fed by modelDirectory), and the resident-mode
19
+ * block (live switch), whose writes ride
20
+ * the bound settingsScope — the host watcher hot-applies them, no reload.
21
+ * Every read degrades: an absent gateway renders the auth block's
22
+ * 'unavailable' state and drops the model surfaces, an
23
+ * unregistered namespace disables the live controls.
24
+ * @module @khorsheed/dsh-local-agent-codex/client
10
25
  */
11
- const PACKAGE_NAME = '@khorsheed/dsh-local-agent-codex';
26
+ import { createElement, useSyncExternalStore } from 'react';
27
+ import { en, NS, zh } from './locales.js';
28
+ import { CodexBundleConfig, CodexCardSection, } from './SettingsCard.js';
29
+ export { CodexBundleConfig, CodexCardSection, CodexSettingsCard } from './SettingsCard.js';
30
+ export { en, NS, zh };
12
31
  /**
13
32
  * Required services: slot registry, command Remote, and locale registry. The
14
33
  * settings scope is NOT a static inject — its service name differs per host
15
34
  * line (`settingsScope` on 0.1.5, `configForms` on rc.1), so a static entry
16
35
  * would pend the whole client plugin on the other line; bindSettingsScope
17
36
  * probes the serving one instead (the settings client package is a declared
18
- * client inject, so one of them is always already up).
37
+ * client inject, so one of them is always already up). The card track probes
38
+ * `configForms` through the same kind of deferred inject (see apply).
19
39
  */
20
40
  export const inject = ['slots', 'remote', 'remote.commands', 'locale'];
21
41
  /**
22
- * Client plugin body: register the dictionaries and the codex settings
23
- * surfaces bound to the local-agent-codex namespace.
42
+ * Client plugin body: register the dictionaries and the codex settings card
43
+ * bound to the local-agent-codex namespace.
24
44
  * @param ctx - client root context.
25
45
  */
26
46
  export function apply(ctx) {
27
47
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'local-agent-codex: dictionaries');
48
+ const t = ctx.locale.bind(NS);
28
49
  const scope = bindSettingsScope(ctx, 'local-agent-codex');
29
50
  // The auth block's copy lives in the family core's dictionary; binding is
30
51
  // stable per namespace and late dictionary registration still resolves.
@@ -34,16 +55,59 @@ export function apply(ctx) {
34
55
  // never matters, and degrade to the auth block's 'unavailable' state when
35
56
  // the core client is absent.
36
57
  const gateway = () => ctx.get('remote.localAgentGateway');
37
- // Both settings surfaces inject the same face.
38
- const cardInject = () => ({
58
+ // The card's inject face minus the hooks compartment — the family-card
59
+ // section consumes this verbatim; the standalone card's slot registration
60
+ // adds the hooks compartment back below.
61
+ const cardFace = () => ({
39
62
  scope,
40
- hooks: { settings: scope },
41
63
  authT,
42
- renderModelPicker: props => ctx.get('localAgentUi')?.renderHarnessModelPicker('codex', props),
43
- // The harness's memberless model surface; absent gateway/broker
44
- // degrades to the bare input with its recent-models suggestions.
45
- harnessModel: () => gateway()?.harnessModel('codex').then(result => (result.ok ? result.value : undefined))
46
- ?? Promise.resolve(undefined),
64
+ // The curated model list's read/write faces, over the same Remotes.
65
+ modelList: {
66
+ read: () => {
67
+ const gw = gateway();
68
+ if (gw === undefined || typeof gw.modelList !== 'function')
69
+ return Promise.resolve(undefined);
70
+ return gw.modelList('codex').then(result => (result.ok ? result.value : undefined));
71
+ },
72
+ write: (models) => {
73
+ const gw = gateway();
74
+ if (gw === undefined || typeof gw.configureModelList !== 'function')
75
+ return Promise.resolve(undefined);
76
+ return gw.configureModelList('codex', models).then(result => (result.ok ? result.value : undefined));
77
+ },
78
+ },
79
+ // The endpoint's served catalog for the add dialog. A core predating
80
+ // modelDirectory leaves the dialog with its reason.
81
+ modelDirectory: (refresh) => {
82
+ const gw = gateway();
83
+ if (gw === undefined || typeof gw.modelDirectory !== 'function')
84
+ return Promise.resolve(undefined);
85
+ return gw.modelDirectory('codex', undefined, refresh ?? false).then(result => (result.ok ? result.value : undefined));
86
+ },
87
+ // The verify button's probe, over the session-less gateway Remote. A core
88
+ // predating `checkEndpoint` hides the button (declare-and-degrade).
89
+ checkEndpoint: () => {
90
+ const gw = gateway();
91
+ if (gw === undefined || typeof gw.checkEndpoint !== 'function')
92
+ return Promise.resolve(undefined);
93
+ return gw.checkEndpoint('codex').then(result => (result.ok ? result.value : undefined));
94
+ },
95
+ // The credential form's read/write face, over the same session-less
96
+ // Remotes. Absent on a core that predates them — the card hides the form.
97
+ endpointAuth: {
98
+ read: () => {
99
+ const gw = gateway();
100
+ if (gw === undefined || typeof gw.endpointAuth !== 'function')
101
+ return Promise.resolve(undefined);
102
+ return gw.endpointAuth('codex').then(result => (result.ok ? result.value : undefined));
103
+ },
104
+ write: (fields) => {
105
+ const gw = gateway();
106
+ if (gw === undefined || typeof gw.configureEndpoint !== 'function')
107
+ return Promise.resolve(undefined);
108
+ return gw.configureEndpoint('codex', fields).then(result => (result.ok ? result.value : undefined));
109
+ },
110
+ },
47
111
  auth: {
48
112
  status: name =>
49
113
  // The Remote declares (name, scope?) and the client enforces exact arity:
@@ -54,35 +118,57 @@ export function apply(ctx) {
54
118
  .then(result => (result.ok ? result.value?.result.text : undefined)),
55
119
  },
56
120
  });
57
- // The settings surface follows the host line AND the install shape — three
58
- // tracks, all riding slots.inject so each fires only where its declaration
59
- // exists. Standalone install: the package's own Plugins-page detail view
60
- // (`plugins.bundle.config`, keyed by package name). Installed as a family
61
- // member: the member is no longer the profile's direct dependency, so only
62
- // the bundle gets a detail view — the card rides the row-level slot
63
- // (`plugins.row.config`, keyed `<bundle package>#<row id>` with the row id
64
- // the bundle's patch declares) and the bundle's page gains a configure
65
- // entry opening it. 0.1.5: the legacy configurable-plugins tab card
66
- // (`settings.plugin.item`, keyed by the settings namespace) through a
67
- // string-keyed duck narrow of the same service (the slot name is gone from
68
- // the alpha.2 SlotMap).
69
- ctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({
70
- name: 'plugins.bundle.config',
71
- key: PACKAGE_NAME,
72
- locale: NS,
73
- inject: cardInject,
74
- }, CodexBundleConfig));
75
- ctx.slots.inject('plugins.row.config', () => ctx.slots.register({
76
- name: 'plugins.row.config',
77
- key: '@khorsheed/dsh-bundle-local-agent#local-agent-codex',
78
- locale: NS,
79
- inject: cardInject,
80
- }, CodexBundleConfig));
81
- const legacy = ctx.slots;
82
- legacy.inject('settings.plugin.item', () => legacy.register({
83
- name: 'settings.plugin.item',
84
- key: 'local-agent-codex',
85
- locale: NS,
86
- inject: cardInject,
87
- }, CodexSettingsCard));
121
+ // The settings card injects this face.
122
+ const cardInject = () => ({ ...cardFace(), hooks: { settings: scope } });
123
+ // The family-card section render bypasses the slot renderer's inject.hooks
124
+ // materialization, so bind the scope's selector hook here with the same
125
+ // semantics (a uSES subscription over the scope's own subscribe/getSnapshot).
126
+ const subscribeScope = (listener) => scope.subscribe(listener);
127
+ const readScope = () => scope.getSnapshot();
128
+ const useSettings = (select) => select(useSyncExternalStore(subscribeScope, readScope));
129
+ // One Plugins-page seat gated on the SERVED NAMESPACE, never on the install
130
+ // shape: whichever composition carries the `local-agent-codex` row —
131
+ // standalone install, family bundle, or mode pack — serves the row's
132
+ // volatile Config under the row id, and `whileServed` keeps the
133
+ // registration alive for exactly that span (the official companion
134
+ // convention), so a composition without the row shows no trace of the card.
135
+ // This replaces the retired per-shape seats: `plugins.row.config` hardcoded
136
+ // one carrier bundle into its key, and the host deleted
137
+ // `settings.plugin.item`. The probe rides a deferred inject because
138
+ // configForms exists only on the alpha.2+ host line; a 0.1.5 host never
139
+ // fires it, and a configForms predating whileServed (0.1.6-alpha.2) skips
140
+ // the card.
141
+ ctx.inject(['configForms'], (formsCtx) => {
142
+ const forms = formsCtx.configForms;
143
+ if (typeof forms.whileServed !== 'function')
144
+ return;
145
+ return forms.whileServed(['local-agent-codex'], () => {
146
+ // The family card owns the Plugins-page seat when the core's client
147
+ // provides it: a core new enough answers registerHarnessCard, and this
148
+ // package renders as a section of the「本机 Agent 委派」card instead of
149
+ // its own entry. The probe runs at serve time — a host round-trip past
150
+ // client plugin boot — so a composed core has already provided the
151
+ // service; an old or absent core keeps the standalone card below.
152
+ const ui = ctx.get('localAgentUi');
153
+ if (typeof ui?.registerHarnessCard === 'function') {
154
+ return ui.registerHarnessCard({
155
+ name: 'codex',
156
+ // The retired standalone card's order, kept as the section order.
157
+ order: 120,
158
+ locale: NS,
159
+ label: () => t('card.title'),
160
+ render: ({ active, useSessions }) => createElement(CodexCardSection, { active, useSessions, ...cardFace(), useSettings, t }),
161
+ });
162
+ }
163
+ return ctx.slots.inject('plugins.item', () => ctx.slots.register({
164
+ name: 'plugins.item',
165
+ id: 'local-agent-codex',
166
+ // Community cards sort after the official settings pages (orders 10-40).
167
+ order: 120,
168
+ label: () => t('card.title'),
169
+ locale: NS,
170
+ inject: cardInject,
171
+ }, CodexBundleConfig));
172
+ });
173
+ });
88
174
  }
@@ -8,18 +8,23 @@ export declare const zh: {
8
8
  'card.expand': string;
9
9
  'card.collapse': string;
10
10
  'auth.title': string;
11
- 'model.title': string;
12
- 'model.info.aria': string;
13
- 'model.info': string;
14
- 'model.placeholder': string;
15
- 'model.menu': string;
16
- 'model.menuDefault': string;
17
- 'model.menuDefault.cli-config': string;
18
- 'model.menuDefault.cli-builtin': string;
19
- 'model.menuDefault.last-observed': string;
20
- 'model.save': string;
21
- 'model.applied': string;
22
- 'model.error': string;
11
+ 'endpoint.info.aria': string;
12
+ 'endpoint.info': string;
13
+ 'endpoint.baseUrl': string;
14
+ 'endpoint.baseUrl.placeholder': string;
15
+ 'endpoint.apiKey': string;
16
+ 'endpoint.secret.configured': string;
17
+ 'endpoint.secret.placeholder': string;
18
+ 'endpoint.configured': string;
19
+ 'endpoint.save': string;
20
+ 'endpoint.applied': string;
21
+ 'endpoint.error': string;
22
+ 'endpoint.check': string;
23
+ 'endpoint.checking': string;
24
+ 'endpoint.checkDirtyHint': string;
25
+ 'endpoint.checkUnavailable': string;
26
+ 'endpoint.unavailable': string;
27
+ 'endpoint.checkFailed': string;
23
28
  'live.title': string;
24
29
  'live.info.aria': string;
25
30
  'live.info': string;
@@ -42,18 +47,23 @@ export declare const en: {
42
47
  'card.expand': string;
43
48
  'card.collapse': string;
44
49
  'auth.title': string;
45
- 'model.title': string;
46
- 'model.info.aria': string;
47
- 'model.info': string;
48
- 'model.placeholder': string;
49
- 'model.menu': string;
50
- 'model.menuDefault': string;
51
- 'model.menuDefault.cli-config': string;
52
- 'model.menuDefault.cli-builtin': string;
53
- 'model.menuDefault.last-observed': string;
54
- 'model.save': string;
55
- 'model.applied': string;
56
- 'model.error': string;
50
+ 'endpoint.info.aria': string;
51
+ 'endpoint.info': string;
52
+ 'endpoint.baseUrl': string;
53
+ 'endpoint.baseUrl.placeholder': string;
54
+ 'endpoint.apiKey': string;
55
+ 'endpoint.secret.configured': string;
56
+ 'endpoint.secret.placeholder': string;
57
+ 'endpoint.configured': string;
58
+ 'endpoint.save': string;
59
+ 'endpoint.applied': string;
60
+ 'endpoint.error': string;
61
+ 'endpoint.check': string;
62
+ 'endpoint.checking': string;
63
+ 'endpoint.checkDirtyHint': string;
64
+ 'endpoint.checkUnavailable': string;
65
+ 'endpoint.unavailable': string;
66
+ 'endpoint.checkFailed': string;
57
67
  'live.title': string;
58
68
  'live.info.aria': string;
59
69
  'live.info': string;
@@ -3,23 +3,28 @@
3
3
  export const NS = 'local-agent-codex';
4
4
  /** Simplified Chinese dictionary (the key-set source of truth). */
5
5
  export const zh = {
6
- 'card.title': 'Local Agent · Codex',
7
- 'card.description': 'Codex 委派:登录授权与常驻模式(live)',
6
+ 'card.title': '本机 Codex 委派',
7
+ 'card.description': 'Codex 委派:端点凭据与常驻模式(live)',
8
8
  'card.expand': '展开',
9
9
  'card.collapse': '收起',
10
10
  'auth.title': '认证',
11
- 'model.title': '默认模型',
12
- 'model.info.aria': '默认模型说明',
13
- 'model.info': '留空 = 跟随默认(输入框里显示的就是当前跟随的模型)。从列表选择或手动输入一个模型名后保存,之后的每轮委派都以它起 CLI(一次性轮次走 `codex exec -m <模型>`,常驻模式走 app-server 的 `-c model=…`)。成员会话里还可用作曲器按会话切换,不影响这里的默认。改动在下一轮委派生效,进行中的轮次不受影响;评测 run 的条件在建立时冻结,中途换模型会被下一轮的模型回读判为 misattributed 而拦下。',
14
- 'model.placeholder': '留空 = 不传模型,用 CLI 自己的默认',
15
- 'model.menu': '选择模型',
16
- 'model.menuDefault': '默认(跟随 CLI 内置默认)',
17
- 'model.menuDefault.cli-config': '默认(跟随 CLI 配置:{model})',
18
- 'model.menuDefault.cli-builtin': '默认(跟随 CLI 默认:{model})',
19
- 'model.menuDefault.last-observed': '默认(跟随 CLI 内置默认(最近:{model}))',
20
- 'model.save': '保存',
21
- 'model.applied': '已保存,下一轮委派生效;进行中的轮次不受影响',
22
- 'model.error': '保存失败,请重试',
11
+ 'endpoint.info.aria': '端点凭据说明',
12
+ 'endpoint.info': '这里保存的就是默认 scope 的 endpoint-auth.json(0600,只存本机),它优先于 profile 的 YAML 配置行;宿主 shell 的环境变量从不被读取。配置 API Key 后每轮委派以自定义 provider(wire_api=responses)路由到端点;端点地址留空 = OpenAI 官方端点。密钥绝不写进 profile 文件,也不进入评测条件指纹。没有网页登录:容器轮与宿主轮用同一套凭据,容器里不需要再授权一次。保存对下一轮委派生效,进行中的轮次不受影响。',
13
+ 'endpoint.baseUrl': '端点地址(baseUrl)',
14
+ 'endpoint.baseUrl.placeholder': '留空 = OpenAI 官方端点',
15
+ 'endpoint.apiKey': 'API Key',
16
+ 'endpoint.secret.configured': '已配置 · 输入新值更换,留空保存 = 清除',
17
+ 'endpoint.secret.placeholder': '未配置',
18
+ 'endpoint.configured': '已配置',
19
+ 'endpoint.save': '保存凭据',
20
+ 'endpoint.applied': '已保存,下一轮委派生效;进行中的轮次不受影响',
21
+ 'endpoint.error': '保存失败,请重试',
22
+ 'endpoint.check': '验证端点并获取模型',
23
+ 'endpoint.checking': '验证中…',
24
+ 'endpoint.checkDirtyHint': '有未保存的修改,先保存凭据再验证',
25
+ 'endpoint.checkUnavailable': '当前核心不支持端点验证(请升级 local-agent 核心包)',
26
+ 'endpoint.unavailable': '当前核心不支持凭据配置(请升级 local-agent 核心包)',
27
+ 'endpoint.checkFailed': '验证失败,请重试',
23
28
  'live.title': '常驻模式(live)',
24
29
  'live.info.aria': '常驻模式说明',
25
30
  'live.info': '常驻模式 = 成员进程常驻:输出实时流入成员会话、取消不杀进程、崩溃后自动续上原会话;关闭则每轮独立进程,跑完一次性出结果。设置为全局偏好,对所有后续委派生效。',
@@ -29,23 +34,28 @@ export const zh = {
29
34
  };
30
35
  /** English dictionary, checked complete against the zh key set. */
31
36
  export const en = {
32
- 'card.title': 'Local Agent · Codex',
33
- 'card.description': 'Codex delegation: sign-in and resident (live) mode',
37
+ 'card.title': 'Local Codex Delegation',
38
+ 'card.description': 'Codex delegation: endpoint credential and resident (live) mode',
34
39
  'card.expand': 'Expand',
35
40
  'card.collapse': 'Collapse',
36
41
  'auth.title': 'Authentication',
37
- 'model.title': 'Default model',
38
- 'model.info.aria': 'About the default model',
39
- 'model.info': "Blank = follow the default (the input shows the model it currently follows). Pick from the list or type a model name by hand and save, and every later delegation round starts the CLI with it (`codex exec -m <model>` for one-shot rounds, `-c model=…` on the resident app-server). Inside a member session the composer can also switch the model per session, without touching this default. A change applies to the next round and never disturbs a round in flight; an evaluation run freezes its condition at setup, so switching mid-run is caught by the model read-back on the next round and fails the run as misattributed.",
40
- 'model.placeholder': "Blank = pass no model, keep the CLI's own default",
41
- 'model.menu': 'Pick a model',
42
- 'model.menuDefault': "Default (follow the CLI's built-in default)",
43
- 'model.menuDefault.cli-config': 'Default (follow the CLI config: {model})',
44
- 'model.menuDefault.cli-builtin': 'Default (follow the CLI default: {model})',
45
- 'model.menuDefault.last-observed': "Default (follow the CLI's built-in default (last: {model}))",
46
- 'model.save': 'Save',
47
- 'model.applied': 'Saved — takes effect on the next delegation round; rounds in flight are unaffected',
48
- 'model.error': 'Save failed, please retry',
42
+ 'endpoint.info.aria': 'About the endpoint credential',
43
+ 'endpoint.info': "What you save here is the default scope's endpoint-auth.json (mode 0600, local only), which outranks the profile's YAML row; the host shell's environment is never read. With an API Key configured, every delegation round routes at the endpoint through a custom provider (wire_api=responses); a blank endpoint means OpenAI's own. Keys never enter the profile document or an evaluation's condition fingerprint. There is no web login: container rounds share the host's credential and nothing is authorized inside a container. Saving applies to the next delegation round; rounds in flight are unaffected.",
44
+ 'endpoint.baseUrl': 'Endpoint (baseUrl)',
45
+ 'endpoint.baseUrl.placeholder': "Blank = OpenAI's own endpoint",
46
+ 'endpoint.apiKey': 'API Key',
47
+ 'endpoint.secret.configured': 'Configured · type to replace, save blank to clear',
48
+ 'endpoint.secret.placeholder': 'Not configured',
49
+ 'endpoint.configured': 'Configured',
50
+ 'endpoint.save': 'Save credential',
51
+ 'endpoint.applied': 'Saved — takes effect on the next delegation round; rounds in flight are unaffected',
52
+ 'endpoint.error': 'Save failed, please retry',
53
+ 'endpoint.check': 'Verify endpoint & list models',
54
+ 'endpoint.checking': 'Verifying…',
55
+ 'endpoint.checkDirtyHint': 'Unsaved changes — save the credential before verifying',
56
+ 'endpoint.checkUnavailable': 'This core cannot verify endpoints (upgrade the local-agent core package)',
57
+ 'endpoint.unavailable': 'This core cannot configure credentials (upgrade the local-agent core package)',
58
+ 'endpoint.checkFailed': 'Verification failed, please retry',
49
59
  'live.title': 'Resident mode (live)',
50
60
  'live.info.aria': 'About resident mode',
51
61
  'live.info': 'Resident mode keeps the member process alive: output streams into the member session in real time, cancelling does not kill the process, and a crash resumes the same session. Off means every round runs as an independent one-shot process. The setting is global and applies to every later delegation.',
@@ -13,13 +13,22 @@
13
13
  * it too (a deliberate user re-enable conflicts loud, by design).
14
14
  * @module @khorsheed/dsh-local-agent-codex/codex-cli-provider
15
15
  */
16
- import type { ContentBlock, TokenUsage } from '@deepseek-ai/dsh-llm';
16
+ import type { ContentBlock, ReasoningEffortId, TokenUsage } from '@deepseek-ai/dsh-llm';
17
17
  import { type ResolvedSubagentStartRequest, type SubagentCapabilities, type SubagentProvider, type SubagentRun, type SubagentStartRequest, type SubagentStopReason } from '@deepseek-ai/dsh-subagent';
18
18
  import type { Context } from '@deepseek-ai/cordis';
19
19
  import type { Session } from '@deepseek-ai/dsh-session';
20
20
  import type { SubprocessHandle, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess';
21
21
  import type { DelegationExecTarget, LocalAgentToolCalls } from '@khorsheed/dsh-local-agent/types';
22
22
  import type { CodexLiveDriver } from './live-driver.ts';
23
+ import { type CodexEndpointAuth } from './endpoint-auth.ts';
24
+ /**
25
+ * The synthesized result for a call whose result the stream never recorded
26
+ * (a killed run, or a result-less item type like web_search_call). The
27
+ * released-log contract resolves every advertised call before its step
28
+ * closes, so an interrupted call folds with this error result. Same text as
29
+ * the kimi provider's placeholder, for family consistency.
30
+ */
31
+ export declare const CODEX_INTERRUPTED_TOOL_RESULT = "\uFF08\u5DE5\u5177\u8C03\u7528\u88AB\u4E2D\u65AD\uFF0C\u672A\u80FD\u8BB0\u5F55\u8FD4\u56DE\u7ED3\u679C\uFF09";
23
32
  /** Default POSIX grace between subprocess termination tiers. */
24
33
  export declare const DEFAULT_DISPOSE_GRACE_MS = 3000;
25
34
  /**
@@ -71,6 +80,7 @@ export declare class CodexCliProvider implements SubagentProvider {
71
80
  private readonly live?;
72
81
  private readonly model?;
73
82
  private readonly overrides?;
83
+ private readonly endpoint?;
74
84
  readonly name = "codex-local";
75
85
  readonly capabilities: SubagentCapabilities;
76
86
  readonly inheritsParentContext = false;
@@ -81,17 +91,25 @@ export declare class CodexCliProvider implements SubagentProvider {
81
91
  * while a retiring generation still hosts it).
82
92
  * @param model - resolver for the configured model, read PER ROUND so a
83
93
  * settings-card write takes effect on the next delegation without a
84
- * reload. Undefined (the resolver absent, or returning undefined) leaves
85
- * the argv exactly as it was before the key existed — the scoped
86
- * config.toml's own `model` then decides, as it always did.
94
+ * reload; it receives the round's scope (a named scope's curated list
95
+ * answers first). Undefined (the resolver absent, or returning
96
+ * undefined) leaves the argv exactly as it was before the key existed —
97
+ * the scoped config.toml's own `model` then decides, as it always did.
87
98
  * @param overrides - resolver for the member's session-level model override
88
99
  * (the composer picker, owned by the model broker). It outranks BOTH the
89
100
  * delegation's recorded model and the configured one: on the exec path it
90
101
  * re-points the next round's `-m`, and on the live path it is the member's
91
102
  * start model — a resident runtime bound to a different model is retired
92
103
  * so the round respawns onto the override.
104
+ * @param endpoint - resolver for the scope's endpoint credential, read PER
105
+ * ROUND so a settings-card write takes effect on the next delegation
106
+ * without a reload. A resolution carrying a key routes the round at the
107
+ * relay (a custom `model_providers` entry on the argv, the key in the
108
+ * env); with no key the spawn carries no credential at all.
93
109
  */
94
- constructor(ctx: Context, sandbox?: CodexSandbox, live?: CodexLiveDriver | ((childSessionId: string) => CodexLiveDriver | undefined) | undefined, model?: (() => string | undefined) | undefined, overrides?: ((childSessionId: string) => string | undefined) | undefined);
110
+ constructor(ctx: Context, sandbox?: CodexSandbox, live?: CodexLiveDriver | ((childSessionId: string) => CodexLiveDriver | undefined) | undefined, model?: ((scope?: string) => string | undefined) | undefined, overrides?: ((childSessionId: string) => string | undefined) | undefined, endpoint?: ((scopeHome: string) => Promise<CodexEndpointAuth>) | undefined);
111
+ /** The scope's endpoint credential for this round; absent resolver = never. */
112
+ private resolveEndpoint;
95
113
  /** Resolve the live driver for one round's member, if live is on for it. */
96
114
  private liveDriver;
97
115
  /** Per-round member-channel registration for the exec path (see {@link registerCodexMemberRun}). */
@@ -116,6 +134,13 @@ export interface CodexCliRunSpec {
116
134
  readonly exec?: DelegationExecTarget | undefined;
117
135
  /** Resolved endpoint label for diagnostics; absent means the CLI default. */
118
136
  readonly endpointLabel?: string | undefined;
137
+ /**
138
+ * The endpoint credential's `-c` config overrides (a custom
139
+ * `model_providers` entry plus the provider selection), spliced next to
140
+ * the member override. Empty when the round carries no endpoint
141
+ * credential — the argv is then byte-identical to the pre-endpoint shape.
142
+ */
143
+ readonly endpointArgv?: readonly string[] | undefined;
119
144
  /** Sandbox policy passed to `codex exec --sandbox`. */
120
145
  readonly sandbox: CodexSandbox;
121
146
  /**
@@ -291,7 +316,8 @@ export declare class CodexStreamParser implements CodexStreamFoldState {
291
316
  * not put one on the wire; the settle path then reads the rollout's
292
317
  * turn_context). Item types covered: `reasoning` (thinking), `agent_message`
293
318
  * (reply text), `command_execution` (shell command with aggregated output),
294
- * `web_search_call`, and `function_call_output`. The last `agent_message`
319
+ * `web_search_call`, `custom_tool_call`/`custom_tool_call_output` (deferred
320
+ * and dynamic tools), and `function_call_output`. The last `agent_message`
295
321
  * wins as the run output (the final answer), and `turn.completed` carries the
296
322
  * turn's usage. Malformed lines are skipped.
297
323
  * @param stream - the collected stdout NDJSON text.
@@ -319,6 +345,17 @@ export declare function parseCodexJsonStream(stream: string): {
319
345
  * @returns the published run after the child starts.
320
346
  */
321
347
  export declare function startCodexCliRun(request: SubagentStartRequest, spec: CodexCliRunSpec): Promise<SubagentRun>;
348
+ /**
349
+ * Write the round's request/header when the config differs from the child
350
+ * log's last one (the host logs changed snapshots, not per-call duplicates):
351
+ * the first write is `initial`, a differing one `change`, an identical one
352
+ * skipped. Context Lens reads the child log's config from these headers.
353
+ */
354
+ export declare function appendCodexRequestHeader(childSession: Session, config: {
355
+ provider: string;
356
+ model: string;
357
+ reasoningEffort?: ReasoningEffortId;
358
+ }): void;
322
359
  /** One assistant-role message event, attributed to the codex route. */
323
360
  export declare function codexAssistantEvent(blocks: readonly ContentBlock[]): import("@deepseek-ai/dsh-llm").AssistantMessage;
324
361
  /**
@@ -327,8 +364,17 @@ export declare function codexAssistantEvent(blocks: readonly ContentBlock[]): im
327
364
  * assembler requires: without a boundary the step never enters the location
328
365
  * index, the line's assistant message resolves to a turn-level location, and
329
366
  * the real-time view renders nothing (only a full rebuild, which mints step
330
- * drafts from the explicit coordinates, recovers it). Tool cards escape that
331
- * fate only because their definition does not consult the step location.
367
+ * drafts from the explicit coordinates, recovers it).
368
+ *
369
+ * A tool line folds the NATIVE lifecycle inside its one step: an assistant
370
+ * message advertising the tool-call block, the `tool/call`, then its
371
+ * `tool/result` — the released-log reader pairs call and result against the
372
+ * advertisement and resolves every advertised call before the step closes.
373
+ * Every fold sees a COMPLETED item (the exec live mirror holds the volatile
374
+ * last line; the live driver folds at item/completed), so a result the
375
+ * stream has not recorded by fold time will never arrive — the call folds
376
+ * with the synthesized interrupted result instead of leaving the step
377
+ * unresolvable.
332
378
  * @returns whether the line folded (false when the options skipped it).
333
379
  */
334
380
  export declare function appendCodexTranscriptLine(childSession: Session, turn: number, step: number, line: CodexTranscriptLine, usage: TokenUsage | undefined): boolean;