@ai-workbench/remote-agent 0.0.0-stage → 0.8.1

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.
Files changed (46) hide show
  1. package/README.md +58 -2
  2. package/cordis.patch.yml +3 -0
  3. package/dist/acp.d.ts +54 -0
  4. package/dist/acp.js +341 -0
  5. package/dist/agents.d.ts +15 -0
  6. package/dist/agents.js +4 -0
  7. package/dist/broker.d.ts +67 -0
  8. package/dist/broker.js +397 -0
  9. package/dist/client.js +36 -0
  10. package/dist/connect-contract.d.ts +26 -0
  11. package/dist/connect-contract.js +1 -0
  12. package/dist/conversation-details.d.ts +47 -0
  13. package/dist/conversation-details.js +319 -0
  14. package/dist/host-layout.d.ts +8 -0
  15. package/dist/host-layout.js +9 -0
  16. package/dist/index.d.ts +114 -0
  17. package/dist/index.js +360 -0
  18. package/dist/input.d.ts +12 -0
  19. package/dist/input.js +49 -0
  20. package/dist/interaction.d.ts +19 -0
  21. package/dist/interaction.js +1 -0
  22. package/dist/job.d.ts +17 -0
  23. package/dist/job.js +57 -0
  24. package/dist/mapping.d.ts +46 -0
  25. package/dist/mapping.js +230 -0
  26. package/dist/oneshot.d.ts +13 -0
  27. package/dist/oneshot.js +78 -0
  28. package/dist/presentation.d.ts +6 -0
  29. package/dist/presentation.js +45 -0
  30. package/dist/provider-locale.d.ts +19 -0
  31. package/dist/provider-locale.js +73 -0
  32. package/dist/provider.d.ts +144 -0
  33. package/dist/provider.js +164 -0
  34. package/dist/readiness.d.ts +28 -0
  35. package/dist/readiness.js +192 -0
  36. package/dist/remote-details.d.ts +26 -0
  37. package/dist/remote-details.js +250 -0
  38. package/dist/remote.d.ts +34 -0
  39. package/dist/remote.js +51 -0
  40. package/dist/sdk-ssh-wrapper.d.ts +2 -0
  41. package/dist/sdk-ssh-wrapper.js +34 -0
  42. package/dist/workspace-notice.d.ts +47 -0
  43. package/dist/workspace-notice.js +273 -0
  44. package/locale/en.json +10 -0
  45. package/locale/zh.json +10 -0
  46. package/package.json +97 -4
@@ -0,0 +1,319 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const redactionMarkers = new WeakSet();
3
+ function redaction(fields) {
4
+ const marker = { redacted: true, ...fields };
5
+ redactionMarkers.add(marker);
6
+ return marker;
7
+ }
8
+ /** Only objects created by this module are trusted as redaction markers. */
9
+ export function isSanitizedRedaction(value) {
10
+ return value !== null && typeof value === 'object' && redactionMarkers.has(value);
11
+ }
12
+ const sensitiveWords = new Set(['auth', 'token', 'tokens', 'password', 'passwords', 'passwd', 'secret', 'secrets', 'authorization', 'cookie', 'cookies', 'credential', 'credentials', 'key', 'keys']);
13
+ const sensitiveCompounds = new Set([
14
+ 'apikey', 'privatekey', 'publickey', 'accesskey', 'secretkey', 'sshkey', 'encryptionkey', 'signingkey',
15
+ 'devicecode', 'usercode', 'onetimecode', 'verificationcode', 'codeverifier', 'authcode', 'authorizationcode', 'authpayload', 'authorizationpayload',
16
+ 'accesstoken', 'refreshtoken', 'idtoken', 'apitoken', 'authtoken', 'sessiontoken', 'bearertoken',
17
+ 'clientsecret', 'tokenpayload', 'tokenvalue', 'passwordpayload', 'passwordvalue', 'secretpayload', 'secretvalue',
18
+ 'credentialpayload', 'credentialspayload', 'cookiepayload', 'keypayload',
19
+ ]);
20
+ // These whole words are safe despite containing a credential-like substring.
21
+ // redactedKeys is the recorder's explicit entry-wrapper marker and is stable
22
+ // across repeated sanitization. It is not an exception for arbitrary values.
23
+ const safeWholeFieldWords = new Set(['author', 'monkey', 'keyboard', 'secretary', 'keyword', 'keywords', 'tokenizer', 'tokenization', 'redactedkeys']);
24
+ // A recognized suffix supplies a field boundary after case information is lost.
25
+ // Prefixes remain unrestricted: employeePassword and customApiKey must not depend
26
+ // on an inventory of employee/customer/application-specific field prefixes.
27
+ const sensitiveFieldSuffixes = new Set([
28
+ '', 'code', 'payload', 'value', 'values', 'data', 'header', 'headers', 'config', 'configuration',
29
+ 'option', 'options', 'detail', 'details', 'info',
30
+ ]);
31
+ const unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);
32
+ function isSensitiveFieldWord(word) {
33
+ if (safeWholeFieldWords.has(word))
34
+ return false;
35
+ if (sensitiveWords.has(word) || sensitiveCompounds.has(word))
36
+ return true;
37
+ for (const root of sensitiveWords) {
38
+ for (let index = word.indexOf(root); index !== -1; index = word.indexOf(root, index + 1)) {
39
+ // Match roots at the start, or roots ending a field (possibly followed by
40
+ // a known suffix), rather than fuzzy containment at any word position.
41
+ if (index === 0 || sensitiveFieldSuffixes.has(word.slice(index + root.length)))
42
+ return true;
43
+ }
44
+ }
45
+ return false;
46
+ }
47
+ function isSensitiveKey(key) {
48
+ if (safeWholeFieldWords.has(key.toLowerCase()))
49
+ return false;
50
+ // Preserve the original camel/delimiter rule. Anchored field-shape matching
51
+ // additionally recognizes the same common fields after case normalization.
52
+ const camelWords = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase().split(/[^a-z0-9]+/);
53
+ const words = key.toLowerCase().split(/[^a-z0-9]+/);
54
+ return camelWords.some(word => sensitiveWords.has(word)) || words.some(isSensitiveFieldWord)
55
+ || sensitiveCompounds.has(key.replace(/[^a-z0-9]/gi, '').toLowerCase());
56
+ }
57
+ function unsupported(type, reason) {
58
+ return { unsupported: true, type, reason };
59
+ }
60
+ function credentialText(value) {
61
+ return /\bBearer\s+\S+/i.test(value)
62
+ || /\bsk-[a-z\d_-]+/i.test(value)
63
+ || /-----BEGIN (?:[A-Z\d]+ )*PRIVATE KEY-----/i.test(value)
64
+ || /\b(?:gh[pousr]_|github_pat_|xox[baprs]-)[a-z\d_-]+/i.test(value);
65
+ }
66
+ function cleanUrls(value) {
67
+ let changed = false;
68
+ const safe = value.replace(/\b[a-z][a-z\d+.-]*:\/\/[^\s<>"'`]+/gi, raw => {
69
+ let url;
70
+ try {
71
+ url = new URL(raw);
72
+ }
73
+ catch {
74
+ changed = true;
75
+ return '[已脱敏:无法解析URL]';
76
+ }
77
+ const userinfo = /^[a-z][a-z\d+.-]*:\/\/[^/?#]*@/i.test(raw);
78
+ if (!userinfo && !raw.includes('?') && !raw.includes('#'))
79
+ return raw;
80
+ url.username = '';
81
+ url.password = '';
82
+ url.search = '';
83
+ url.hash = '';
84
+ changed = true;
85
+ return url.toString();
86
+ });
87
+ return { value: safe, changed };
88
+ }
89
+ function safeText(value) {
90
+ if (credentialText(value))
91
+ return redaction({ reason: '凭据文本' });
92
+ const url = cleanUrls(value);
93
+ return url.changed ? redaction({ reason: 'URL敏感部分', value: url.value }) : value;
94
+ }
95
+ function safeLabel(value) {
96
+ if (typeof value !== 'string')
97
+ throw new TypeError('Detail observation labels must be strings');
98
+ // Inspect credentials before removing control characters, so a bearer newline cannot evade detection.
99
+ if (credentialText(value))
100
+ return '[已脱敏:凭据文本]';
101
+ const cleaned = value.replace(/[\u0000-\u001f\u007f-\u009f]/g, '');
102
+ if (credentialText(cleaned))
103
+ return '[已脱敏:凭据文本]';
104
+ const url = cleanUrls(cleaned);
105
+ return url.changed ? `[已脱敏:URL敏感部分] ${url.value}` : cleaned;
106
+ }
107
+ /** JSON-only snapshot. Non-JSON input is represented, never coerced or executed. */
108
+ function snapshotPayload(value, ancestors = new Set(), protectModelMaterial = false) {
109
+ if (isSanitizedRedaction(value))
110
+ return value;
111
+ if (value === null || typeof value === 'boolean')
112
+ return value;
113
+ if (typeof value === 'string')
114
+ return safeText(value);
115
+ if (typeof value === 'number')
116
+ return Number.isFinite(value) ? value : unsupported('number', '非有限数值不能表示为JSON');
117
+ if (typeof value !== 'object')
118
+ return unsupported(typeof value, '非JSON值');
119
+ if (ancestors.has(value))
120
+ return unsupported('object', '循环引用');
121
+ let descriptors;
122
+ let isArray;
123
+ try {
124
+ isArray = Array.isArray(value);
125
+ const prototype = Object.getPrototypeOf(value);
126
+ if (!isArray && prototype !== Object.prototype && prototype !== null)
127
+ return unsupported('object', '非JSON普通对象');
128
+ if (Object.getOwnPropertySymbols(value).some(symbol => Object.getOwnPropertyDescriptor(value, symbol)?.enumerable)) {
129
+ return unsupported('object', 'JSON不能表示符号字段');
130
+ }
131
+ descriptors = Object.getOwnPropertyDescriptors(value);
132
+ }
133
+ catch {
134
+ return unsupported('object', '无法读取对象结构');
135
+ }
136
+ if (Object.keys(descriptors).some(key => descriptors[key].enumerable && unsafeKeys.has(key))) {
137
+ return unsupported('object', '不允许的原型字段');
138
+ }
139
+ ancestors.add(value);
140
+ try {
141
+ if (isArray) {
142
+ // Array metadata beyond indexed items is not representable in a JSON array.
143
+ const length = descriptors.length?.value;
144
+ if (Object.keys(descriptors).some(key => descriptors[key].enumerable && (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= length))) {
145
+ return unsupported('array', 'JSON数组不能表示额外字段');
146
+ }
147
+ return Array.from({ length }, (_, index) => {
148
+ const descriptor = descriptors[String(index)];
149
+ return !descriptor ? unsupported('undefined', '数组空位')
150
+ : 'value' in descriptor ? snapshotPayload(descriptor.value, ancestors, protectModelMaterial)
151
+ : unsupported('accessor', '不执行访问器');
152
+ });
153
+ }
154
+ const result = {};
155
+ const entries = [];
156
+ let redactedKeys = false;
157
+ for (const [key, descriptor] of Object.entries(descriptors)) {
158
+ if (!descriptor.enumerable)
159
+ continue;
160
+ const name = safeText(key);
161
+ const value = 'value' in descriptor ? descriptor.value : undefined;
162
+ const normalized = key.replace(/[^a-z0-9]/gi, '').toLowerCase();
163
+ const counterNames = new Set(['inputtokens', 'outputtokens', 'cachedinputtokens', 'cachedreadtokens', 'cachedwritetokens', 'thoughtTokens'.toLowerCase(), 'totaltokens', 'cachewriteinputtokens', 'cachereadinputtokens', 'reasoningoutputtokens']);
164
+ const validCounter = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
165
+ const counter = counterNames.has(normalized) && validCounter(value)
166
+ || normalized === 'tokencount' && value !== null && typeof value === 'object' && Object.values(Object.getOwnPropertyDescriptors(value)).every(d => 'value' in d && validCounter(d.value)) && Object.keys(value).every(k => counterNames.has(k.replace(/[^a-z0-9]/gi, '').toLowerCase()));
167
+ const material = protectModelMaterial && (/(?:headers?|body|oldtext|newtext|aggregatedoutput|stdout|stderr)$/.test(normalized)
168
+ || ['text', 'content', 'prompt', 'input', 'output', 'rawinput', 'rawoutput', 'messages', 'modelinput', 'modeloutput', 'patch', 'diff', 'arguments', 'command', 'commandline', 'filecontent', 'data', 'message', 'usercode', 'devicecode', 'onetimecode'].includes(normalized));
169
+ const child = ((!counter && isSensitiveKey(key)) || material) ? redaction({ reason: '敏感字段' })
170
+ : 'value' in descriptor ? snapshotPayload(descriptor.value, ancestors, protectModelMaterial)
171
+ : unsupported('accessor', '不执行访问器');
172
+ if (typeof name !== 'string')
173
+ redactedKeys = true;
174
+ // Only safe property names may become object keys. The entry form keeps
175
+ // distinct original fields even when multiple names redact identically.
176
+ if (typeof name === 'string')
177
+ result[name] = child;
178
+ entries.push({ name, value: child });
179
+ }
180
+ return redactedKeys ? { redactedKeys: true, entries } : result;
181
+ }
182
+ finally {
183
+ ancestors.delete(value);
184
+ }
185
+ }
186
+ function freezeDeep(value) {
187
+ if (value !== null && typeof value === 'object') {
188
+ for (const child of Object.values(value))
189
+ freezeDeep(child);
190
+ Object.freeze(value);
191
+ }
192
+ return value;
193
+ }
194
+ function cloneFrozen(value) { return freezeDeep(structuredClone(value)); }
195
+ /** No raw values from unclassified events or tool I/O are approved. The
196
+ * structure and original JSON types remain inspectable, including unknown keys. */
197
+ function opaqueStructure(value) {
198
+ if (Array.isArray(value))
199
+ return value.map(opaqueStructure);
200
+ if (value !== null && typeof value === 'object') {
201
+ if (isSanitizedRedaction(value))
202
+ return value;
203
+ return Object.fromEntries(Object.entries(value).map(([name, child]) => [name, opaqueStructure(child)]));
204
+ }
205
+ return redaction({ valueType: value === null ? 'null' : typeof value });
206
+ }
207
+ /** Structural identifiers are approved only at the SDK/ACP envelope and its
208
+ * immediate item/update. Arbitrary result subtrees never inherit that trust. */
209
+ function toolStructure(value, level = 'envelope') {
210
+ if (!value || typeof value !== 'object' || Array.isArray(value))
211
+ return opaqueStructure(value);
212
+ if (isSanitizedRedaction(value))
213
+ return value;
214
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => {
215
+ const field = key.replace(/[^a-z0-9]/gi, '').toLowerCase();
216
+ if (typeof child === 'string' && ['id', 'type', 'kind', 'status', 'code', 'name', 'toolcallid', 'optionid', 'sessionid', 'sessionupdate'].includes(field))
217
+ return [key, child];
218
+ if (typeof child === 'number' && ['exitcode', 'durationms'].includes(field))
219
+ return [key, child];
220
+ if (level === 'envelope' && ['item', 'update', 'toolCall'].includes(key))
221
+ return [key, toolStructure(child, 'item')];
222
+ return [key, opaqueStructure(child)];
223
+ }));
224
+ }
225
+ function catalogStructure(value, level = 'root') {
226
+ if (!value || typeof value !== 'object' || Array.isArray(value))
227
+ return opaqueStructure(value);
228
+ if (isSanitizedRedaction(value))
229
+ return value;
230
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => {
231
+ if (typeof child === 'string' && ((level === 'entry' && ['name', 'description'].includes(key)) || (level === 'input' && key === 'hint') || (level === 'root' && ['sessionId', 'sessionUpdate'].includes(key))))
232
+ return [key, child];
233
+ if (level === 'root' && key === 'update')
234
+ return [key, catalogStructure(child)];
235
+ if (level === 'root' && key === 'availableCommands' && Array.isArray(child))
236
+ return [key, child.map(v => catalogStructure(v, 'entry'))];
237
+ if (level === 'entry' && key === 'input')
238
+ return [key, child === null ? null : catalogStructure(child, 'input')];
239
+ return [key, opaqueStructure(child)];
240
+ }));
241
+ }
242
+ function usageStructure(value, key = '') {
243
+ if (Array.isArray(value))
244
+ return value.map(child => usageStructure(child));
245
+ if (value !== null && typeof value === 'object') {
246
+ if (isSanitizedRedaction(value))
247
+ return value;
248
+ return Object.fromEntries(Object.entries(value).map(([name, child]) => [name, usageStructure(child, name)]));
249
+ }
250
+ const field = key.replace(/[^a-z0-9]/gi, '').toLowerCase();
251
+ if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 && ['inputtokens', 'outputtokens', 'cachedinputtokens', 'cachedreadtokens', 'cachedwritetokens', 'thoughttokens', 'totaltokens', 'cachewriteinputtokens', 'cachereadinputtokens', 'reasoningoutputtokens'].includes(field))
252
+ return value;
253
+ if (key === 'stopReason' && typeof value === 'string' && ['end_turn', 'max_tokens', 'max_turn_requests', 'refusal', 'cancelled'].includes(value))
254
+ return value;
255
+ return opaqueStructure(value);
256
+ }
257
+ /** Sanitize an internal observation without attaching any L wire semantics. */
258
+ export function sanitizeObservation(observation, protectModelMaterial = false) {
259
+ const payload = snapshotPayload(observation.payload, new Set(), protectModelMaterial && observation.kind !== 'commands');
260
+ return freezeDeep({
261
+ kind: observation.kind, severity: observation.severity,
262
+ code: safeLabel(observation.code), summary: safeLabel(observation.summary),
263
+ ...(observation.method === undefined ? {} : { method: safeLabel(observation.method) }),
264
+ payload: !protectModelMaterial ? payload
265
+ : observation.kind === 'commands' ? catalogStructure(payload)
266
+ : observation.kind === 'usage' ? usageStructure(payload)
267
+ : observation.kind === 'need' ? payload
268
+ : observation.kind === 'tool' || ['SDK_ITEM', 'SDK_TOOL', 'ACP_SESSION_UPDATE', 'ACP_IDLE_UPDATE'].includes(observation.code) ? toolStructure(payload)
269
+ : opaqueStructure(payload),
270
+ });
271
+ }
272
+ function validatedScope(scope) {
273
+ for (const name of ['instanceId', 'sessionId']) {
274
+ if (typeof scope[name] !== 'string' || scope[name].trim().length === 0)
275
+ throw new TypeError(`Invalid detail scope ${name}`);
276
+ }
277
+ for (const name of ['epoch', 'turn', 'step']) {
278
+ if (!Number.isInteger(scope[name]) || scope[name] < (name === 'epoch' ? 0 : 1))
279
+ throw new TypeError(`Invalid detail scope ${name}`);
280
+ }
281
+ if (scope.provider !== 'claude' && scope.provider !== 'codex')
282
+ throw new TypeError('Invalid detail scope provider');
283
+ return { instanceId: scope.instanceId, epoch: scope.epoch, sessionId: scope.sessionId, provider: scope.provider, turn: scope.turn, step: scope.step };
284
+ }
285
+ /** Internal diagnostic recorder; a sink is not an assertion that an L wire API exists. */
286
+ export class ConversationDetails {
287
+ scope;
288
+ sink;
289
+ records = [];
290
+ tail = Promise.resolve();
291
+ firstFailure;
292
+ constructor(scope, sink) {
293
+ this.scope = freezeDeep(validatedScope(scope));
294
+ this.sink = sink;
295
+ }
296
+ record(observation) {
297
+ const record = freezeDeep({
298
+ ...this.scope, version: 1, eventId: randomUUID(), sequence: this.records.length + 1,
299
+ ...sanitizeObservation(observation),
300
+ });
301
+ this.records.push(record);
302
+ this.tail = this.tail.then(async () => {
303
+ try {
304
+ await this.sink.append(cloneFrozen(record));
305
+ }
306
+ catch (error) {
307
+ // Keep processing later entries in order, but never turn a failed flush green.
308
+ if (!this.firstFailure)
309
+ this.firstFailure = { error };
310
+ }
311
+ });
312
+ }
313
+ async flush() {
314
+ await this.tail;
315
+ if (this.firstFailure)
316
+ throw this.firstFailure.error;
317
+ }
318
+ snapshot() { return cloneFrozen(this.records); }
319
+ }
@@ -0,0 +1,8 @@
1
+ import { type ConnectTarget } from './provider.ts';
2
+ /** S host-layout 0.3.4 fixed executable conventions, not employee policy defaults. */
3
+ export declare function protectedOneShotLayout(target: ConnectTarget): {
4
+ runner: string;
5
+ runnerKind: "executable";
6
+ root: string;
7
+ codex: string;
8
+ };
@@ -0,0 +1,9 @@
1
+ import { validateTarget } from "./provider.js";
2
+ import { NeedError } from "./remote.js";
3
+ /** S host-layout 0.3.4 fixed executable conventions, not employee policy defaults. */
4
+ export function protectedOneShotLayout(target) {
5
+ validateTarget(target);
6
+ if (!/^emp-[a-z0-9][a-z0-9-]*$/.test(target.username) || target.session.root !== `/Users/${target.username}/.aw/s`)
7
+ throw new NeedError('HOST_LAYOUT_MISMATCH', '远程主机的运行位置需要管理员核对', 'S声明的同一员工HOME/.aw/s与HOME/.aw/oneshot布局');
8
+ return { runner: '/usr/local/aw-host/bin/aw-codex-once', runnerKind: 'executable', root: `/Users/${target.username}/.aw/oneshot`, codex: '/opt/homebrew/bin/codex' };
9
+ }
@@ -0,0 +1,114 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import Schema from '@deepseek-ai/schemastery';
3
+ import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm';
4
+ import { type AcpHooks } from './acp.ts';
5
+ import { type RuntimeProvider } from './provider.ts';
6
+ import { type AgentDefinition } from './agents.ts';
7
+ import { type ImageReader } from './input.ts';
8
+ export declare const name = "ai-workbench-remote-agent";
9
+ export declare const settingsNamespace = "ai-workbench-codex";
10
+ export declare const inject: string[];
11
+ /** Owner-approved defaults; administrators may override through the existing settings. */
12
+ export declare const DEFAULT_CONFIG: Readonly<{
13
+ readonly mode: "session";
14
+ readonly acpMaxMessageBytes: 4194304;
15
+ readonly brokerMaxFrameBytes: 2097152;
16
+ readonly brokerControlMaxBytes: 1048576;
17
+ readonly oneShotSandbox: "workspace-write";
18
+ readonly oneShotApproval: "never";
19
+ }>;
20
+ export declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
21
+ mode: Schema<"session" | "one-shot", "session" | "one-shot", "volatile-defined">;
22
+ acpMaxMessageBytes: Schema<number, number, "volatile-defined">;
23
+ brokerMaxFrameBytes: Schema<number, number, "volatile-defined">;
24
+ brokerControlMaxBytes: Schema<number, number, "volatile-defined">;
25
+ oneShotSandbox: Schema<"read-only" | "workspace-write" | "danger-full-access", "read-only" | "workspace-write" | "danger-full-access", "volatile-defined">;
26
+ oneShotApproval: Schema<"never", "never", "volatile-defined">;
27
+ }>>, Schemastery.ObjectT<NoInfer<{
28
+ mode: Schema<"session" | "one-shot", "session" | "one-shot", "volatile-defined">;
29
+ acpMaxMessageBytes: Schema<number, number, "volatile-defined">;
30
+ brokerMaxFrameBytes: Schema<number, number, "volatile-defined">;
31
+ brokerControlMaxBytes: Schema<number, number, "volatile-defined">;
32
+ oneShotSandbox: Schema<"read-only" | "workspace-write" | "danger-full-access", "read-only" | "workspace-write" | "danger-full-access", "volatile-defined">;
33
+ oneShotApproval: Schema<"never", "never", "volatile-defined">;
34
+ }>>, "plain">;
35
+ export interface PluginConfig {
36
+ mode?: 'session' | 'one-shot';
37
+ acpMaxMessageBytes?: number;
38
+ brokerMaxFrameBytes?: number;
39
+ brokerControlMaxBytes?: number;
40
+ oneShotSandbox?: 'read-only' | 'workspace-write' | 'danger-full-access';
41
+ oneShotApproval?: 'never';
42
+ }
43
+ type LiveConfig = {
44
+ [K in keyof PluginConfig]: PluginConfig[K] | {
45
+ get(): PluginConfig[K];
46
+ };
47
+ };
48
+ /** DSH volatile settings are handles; adapters freeze their values once per invocation. */
49
+ export declare function readConfig(config: LiveConfig): PluginConfig;
50
+ export declare class CodexRemoteAdapter extends LlmAdapter {
51
+ private resolveRuntime;
52
+ private approve?;
53
+ private readonly providerName;
54
+ private readonly agent;
55
+ private readonly readImage?;
56
+ private config;
57
+ private sessions;
58
+ private turns;
59
+ private lifetime;
60
+ constructor(config: PluginConfig | (() => PluginConfig), resolveRuntime: RuntimeProvider, approve?: InvocationApproval | undefined, providerName?: () => string, agent?: AgentDefinition, readImage?: ImageReader | undefined);
61
+ providerInfo(provider: string): {
62
+ id: string;
63
+ name: string;
64
+ };
65
+ listModels(provider: string): Promise<{
66
+ provider: string;
67
+ id: string;
68
+ name: string;
69
+ inputModalities: ("text" | "image")[];
70
+ }[]>;
71
+ providerRetryPolicy(): {
72
+ mode: "normal";
73
+ maxRetries: number;
74
+ retryableCodes: never[];
75
+ initialDelayMs: number;
76
+ maxDelayMs: number;
77
+ jitterRatio: number;
78
+ };
79
+ disposeSession(sessionId: string): Promise<void>;
80
+ dispose(): Promise<void>;
81
+ stream(options: GenerateOptions): AsyncGenerator<StreamChunk>;
82
+ private generate;
83
+ }
84
+ /** One official registration owns both routes, preserving opaque replay ownership. */
85
+ export declare class RemoteAgentsAdapter extends LlmAdapter {
86
+ private adapters;
87
+ constructor(config: () => PluginConfig, makeRuntime: (agent: AgentDefinition) => RuntimeProvider, approve: InvocationApproval, nameFor: (agent: AgentDefinition) => string, readImage?: ImageReader);
88
+ private adapter;
89
+ providerInfo(provider: string): {
90
+ id: string;
91
+ name: string;
92
+ };
93
+ listModels(provider: string): Promise<{
94
+ provider: string;
95
+ id: string;
96
+ name: string;
97
+ inputModalities: ("text" | "image")[];
98
+ }[]>;
99
+ providerRetryPolicy(): {
100
+ mode: "normal";
101
+ maxRetries: number;
102
+ retryableCodes: never[];
103
+ initialDelayMs: number;
104
+ maxDelayMs: number;
105
+ jitterRatio: number;
106
+ };
107
+ stream(options: GenerateOptions): AsyncGenerator<StreamChunk, any, any>;
108
+ disposeSession(sessionId: string): Promise<void>;
109
+ dispose(): Promise<void>;
110
+ }
111
+ export declare function apply(ctx: Context, live: LiveConfig): void;
112
+ type InvocationApproval = (request: Parameters<NonNullable<AcpHooks['approve']>>[0], signal: AbortSignal | undefined, options: GenerateOptions, context?: string) => ReturnType<NonNullable<AcpHooks['approve']>>;
113
+ export declare function dshApproval(ctx: Context): InvocationApproval;
114
+ export {};