@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.
- package/CHANGELOG.md +25 -0
- package/README.en.md +23 -122
- package/README.i18n.yaml +2 -2
- package/README.md +23 -122
- package/lib/client.js +811 -317
- package/lib/index.js +818 -252
- package/lib/types/client/SettingsCard.d.ts +73 -24
- package/lib/types/client/SettingsCard.js +110 -140
- package/lib/types/client/icons.d.ts +1 -1
- package/lib/types/client/index.d.ts +6 -26
- package/lib/types/client/index.js +136 -50
- package/lib/types/client/locales.d.ts +34 -24
- package/lib/types/client/locales.js +38 -28
- package/lib/types/codex-cli-provider.d.ts +54 -8
- package/lib/types/codex-cli-provider.js +204 -70
- package/lib/types/endpoint-auth.d.ts +194 -0
- package/lib/types/endpoint-auth.js +300 -0
- package/lib/types/index.d.ts +19 -3
- package/lib/types/index.js +127 -28
- package/lib/types/live-driver.d.ts +9 -5
- package/lib/types/live-driver.js +247 -108
- package/lib/types/live-switch.d.ts +7 -0
- package/lib/types/live-switch.js +1 -0
- package/lib/types/model-broker.d.ts +12 -2
- package/lib/types/model-broker.js +51 -6
- package/lib/types/model-catalog.d.ts +53 -9
- package/lib/types/model-catalog.js +95 -12
- package/lib/types/provision.d.ts +12 -27
- package/lib/types/provision.js +19 -40
- package/lib/types/records.d.ts +6 -11
- package/lib/types/records.js +14 -25
- package/locale/en.json +2 -2
- package/locale/zh.json +2 -2
- package/package.json +20 -21
- package/lib/invariant.js +0 -31
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -33
|
@@ -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
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
//
|
|
38
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
// the
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
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
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
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': '
|
|
7
|
-
'card.description': 'Codex
|
|
6
|
+
'card.title': '本机 Codex 委派',
|
|
7
|
+
'card.description': 'Codex 委派:端点凭据与常驻模式(live)',
|
|
8
8
|
'card.expand': '展开',
|
|
9
9
|
'card.collapse': '收起',
|
|
10
10
|
'auth.title': '认证',
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
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
|
|
33
|
-
'card.description': 'Codex delegation:
|
|
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
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
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
|
|
85
|
-
*
|
|
86
|
-
*
|
|
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`,
|
|
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).
|
|
331
|
-
*
|
|
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;
|