@ai-workbench/remote-agent 0.8.21 → 0.8.24

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 (96) hide show
  1. package/README.md +28 -155
  2. package/dist/acp.d.ts +20 -15
  3. package/dist/acp.js +197 -224
  4. package/dist/agents.d.ts +0 -1
  5. package/dist/agents.js +2 -2
  6. package/dist/broker-runtime.d.ts +3 -5
  7. package/dist/broker-runtime.js +45 -342
  8. package/dist/broker.d.ts +6 -31
  9. package/dist/broker.js +42 -113
  10. package/dist/catalog-ssh.d.ts +2 -31
  11. package/dist/catalog-ssh.js +9 -39
  12. package/dist/claude-local.d.ts +1 -1
  13. package/dist/claude-local.js +40 -105
  14. package/dist/client.js +34 -127
  15. package/dist/close-diagnostics.d.ts +6 -20
  16. package/dist/close-diagnostics.js +13 -57
  17. package/dist/connect-contract.d.ts +4 -8
  18. package/dist/conversation-details.d.ts +0 -29
  19. package/dist/conversation-details.js +8 -99
  20. package/dist/employee-endpoint.d.ts +1 -3
  21. package/dist/employee-endpoint.js +8 -39
  22. package/dist/employee-exec.d.ts +4 -3
  23. package/dist/employee-exec.js +25 -13
  24. package/dist/employee-info.d.ts +5 -1
  25. package/dist/employee-info.js +16 -5
  26. package/dist/employee-location.js +3 -4
  27. package/dist/employee-mcp.js +44 -69
  28. package/dist/employee-native.d.ts +1 -3
  29. package/dist/employee-native.js +6 -27
  30. package/dist/employee-presentation.d.ts +0 -5
  31. package/dist/employee-presentation.js +0 -15
  32. package/dist/employee-provider.d.ts +3 -11
  33. package/dist/employee-provider.js +69 -205
  34. package/dist/employee-reasons.js +1 -5
  35. package/dist/employee-shell.d.ts +2 -1
  36. package/dist/employee-shell.js +10 -13
  37. package/dist/employee-ssh.d.ts +1 -3
  38. package/dist/employee-ssh.js +13 -16
  39. package/dist/employee-tools.d.ts +5 -5
  40. package/dist/failure-evidence.d.ts +2 -10
  41. package/dist/failure-evidence.js +12 -61
  42. package/dist/host-workspace.d.ts +4 -2
  43. package/dist/host-workspace.js +1 -1
  44. package/dist/index.d.ts +12 -42
  45. package/dist/index.js +203 -471
  46. package/dist/input.js +2 -3
  47. package/dist/local-user-turn.js +5 -6
  48. package/dist/mapping.d.ts +0 -3
  49. package/dist/mapping.js +0 -34
  50. package/dist/model-catalog.d.ts +2 -41
  51. package/dist/model-catalog.js +24 -282
  52. package/dist/model-configuration.d.ts +0 -3
  53. package/dist/model-configuration.js +8 -47
  54. package/dist/model-presentation.d.ts +1 -0
  55. package/dist/model-presentation.js +2 -2
  56. package/dist/permissions.d.ts +15 -0
  57. package/dist/permissions.js +30 -0
  58. package/dist/preflight-diagnostic.js +11 -22
  59. package/dist/preflight-payload.d.ts +1 -1
  60. package/dist/preflight-payload.js +2 -2
  61. package/dist/presentation.d.ts +0 -1
  62. package/dist/presentation.js +21 -42
  63. package/dist/provider.d.ts +12 -48
  64. package/dist/provider.js +18 -106
  65. package/dist/readiness.js +1 -7
  66. package/dist/remote-details.d.ts +41 -17
  67. package/dist/remote-details.js +84 -381
  68. package/dist/workspace-source.d.ts +1 -25
  69. package/dist/workspace-source.js +1 -138
  70. package/package.json +11 -12
  71. package/dist/catalog-diagnostics.d.ts +0 -16
  72. package/dist/catalog-diagnostics.js +0 -108
  73. package/dist/host-layout.d.ts +0 -8
  74. package/dist/host-layout.js +0 -9
  75. package/dist/job.d.ts +0 -17
  76. package/dist/job.js +0 -57
  77. package/dist/model-defaults.d.ts +0 -29
  78. package/dist/model-defaults.js +0 -62
  79. package/dist/oneshot.d.ts +0 -13
  80. package/dist/oneshot.js +0 -78
  81. package/dist/protocol-boundary.d.ts +0 -13
  82. package/dist/protocol-boundary.js +0 -49
  83. package/dist/provider-locale.d.ts +0 -19
  84. package/dist/provider-locale.js +0 -73
  85. package/dist/recovery-checkpoint.d.ts +0 -6
  86. package/dist/recovery-checkpoint.js +0 -71
  87. package/dist/recovery.d.ts +0 -51
  88. package/dist/recovery.js +0 -61
  89. package/dist/sdk-ssh-wrapper.d.ts +0 -2
  90. package/dist/sdk-ssh-wrapper.js +0 -34
  91. package/dist/stage-timing.d.ts +0 -31
  92. package/dist/stage-timing.js +0 -61
  93. package/dist/workspace-notice.d.ts +0 -47
  94. package/dist/workspace-notice.js +0 -273
  95. package/dist/workspace-source-confirmation.d.ts +0 -4
  96. package/dist/workspace-source-confirmation.js +0 -12
package/dist/provider.js CHANGED
@@ -2,10 +2,8 @@ import { hostWorkspaceGuard } from "./host-workspace.js";
2
2
  import { connectNeedError } from "./connect-need.js";
3
3
  import { loginDiagnostic, loginReason } from "./login-diagnostic.js";
4
4
  import { NeedError } from "./remote.js";
5
- import { CONNECTION_REQUIRED, TEST_HOST_NOTICE } from "./presentation.js";
5
+ import { CONNECTION_REQUIRED } from "./presentation.js";
6
6
  import { CODEX_AGENT } from "./agents.js";
7
- import { StageTiming } from "./stage-timing.js";
8
- import { confirmLegacyWorkspaceSource } from "./workspace-source.js";
9
7
  export function requireLoginCoordinator(api) {
10
8
  if (!['getConnectionState', 'getLoginPromptState', 'requestLoginPrompt', 'refreshLoginPrompts'].every(key => typeof api[key] === 'function'))
11
9
  throw new NeedError('CONNECT_API_MISMATCH', '我需要 connect-api 0.6.1 的统一登录询问与连接状态能力', 'L >=0.6.1');
@@ -120,28 +118,15 @@ export function validateTarget(t) {
120
118
  || ![t.projectRoot, t.session.command, t.session.root, t.session.settingsPath].every(p => typeof p === 'string' && p.startsWith('/'))) {
121
119
  throw new NeedError('CONNECT_API_MISMATCH', '我需要 connect-api 0.3.0 的可信主机、项目和 session 能力;旧目标不能继续使用', 'L 提供的完整 0.3.0 目标');
122
120
  }
123
- hostContextNotice(t);
124
- }
125
- /** Only the declared public check identifiers/statuses, never provider evidence. */
126
- export function hostContextNotice(t) {
127
- const h = t.hostContext;
128
- if (h === undefined)
129
- return;
130
- const checks = (items) => Array.isArray(items) && items.every(item => item && typeof item.id === 'string' && typeof item.status === 'string');
131
- if (!h || (h.enforcement !== 'test' && h.enforcement !== 'production') || (h.checkExitCode !== 0 && h.checkExitCode !== 1) || !checks(h.failedChecks) || !checks(h.unverifiedChecks))
132
- throw new NeedError('CONNECT_API_MISMATCH', '主机检查背景无效,不能假定已通过生产检查', 'L 提供的有效 hostContext');
133
- if (h.enforcement !== 'test')
134
- return;
135
- return TEST_HOST_NOTICE;
136
121
  }
137
122
  export function identity(t) { return `${t.username}@${t.host}:${t.port}`; }
138
123
  /** Reject draft.3 and legacy runtime rather than inventing workspace bindings. */
139
124
  export function requireNativeWorkspace(api) {
140
125
  if (typeof api.getContractInfo !== 'function')
141
- throw new NeedError('DEPENDENCY_REQUIRED', '我需要新版远程主机连接组件提供原生工作区绑定', 'L connect-api 0.7.0 / native-local-v1');
126
+ throw new NeedError('DEPENDENCY_REQUIRED', '我需要新版远程主机连接组件提供原生工作区绑定', 'L connect-api 0.7.0 / ensureReady / resolveSessionProject');
142
127
  const info = api.getContractInfo();
143
- if (info?.apiVersion !== '0.7.0' || info.status !== 'implemented' || info.workspaceBinding !== 'native-local-v1' || !['ensureReady', 'resolveSessionProject', 'acknowledgeWorkspaceNotice'].every(key => typeof api[key] === 'function'))
144
- throw new NeedError('DEPENDENCY_REQUIRED', '我需要新版远程主机连接组件提供原生工作区绑定', 'L connect-api 0.7.0 / native-local-v1');
128
+ if (info?.apiVersion !== '0.7.0' || info.status !== 'implemented' || !['ensureReady', 'resolveSessionProject'].every(key => typeof api[key] === 'function'))
129
+ throw new NeedError('DEPENDENCY_REQUIRED', '我需要新版远程主机连接组件提供原生工作区绑定', 'L connect-api 0.7.0 / ensureReady / resolveSessionProject');
145
130
  }
146
131
  /** Authorization stays in L; ensureReady only checks and records the need. */
147
132
  export async function offerLogin(connect, request, currentApi, agent = CODEX_AGENT) {
@@ -189,32 +174,20 @@ export function connectProvider(get, options = {}) {
189
174
  let employee;
190
175
  const admissions = new WeakMap();
191
176
  const preflight = async (request) => {
192
- request.timing ??= new StageTiming();
193
177
  request.signal?.throwIfAborted();
194
178
  const api = get();
195
179
  if (!api)
196
180
  throw new NeedError('DEPENDENCY_REQUIRED', CONNECTION_REQUIRED, 'aiWorkbenchConnect');
197
181
  requireNativeWorkspace(api);
198
182
  requireLoginCoordinator(api);
199
- if (request.mode === 'one-shot' && !agent.oneShot)
200
- throw new NeedError('MODE_UNSUPPORTED', '此远程模型仅支持会话模式', '使用会话模式');
201
- const confirmedSource = options.workspaceSource ? await confirmLegacyWorkspaceSource(get, api, request, options.workspaceSource) : undefined;
202
183
  let sourceStage = 'input-acceptance';
203
- if (confirmedSource && !request.recordEarlyFailure)
204
- request.recordEarlyFailure = (error, stage) => confirmedSource.recordFailure(error, stage);
205
184
  try {
206
- confirmedSource?.assertCurrent();
207
185
  if (options.employee) {
208
186
  const module = await import("./employee-provider.js");
209
- request.localAcceptance = await module.acceptEmployeeUserTurn(get, options.employee, agent, api, request);
210
- }
211
- confirmedSource?.assertCurrent();
212
- if (confirmedSource) {
213
- const previous = request.localAdmissionCheck;
214
- request.localAdmissionCheck = () => { confirmedSource.assertCurrent(); previous?.(); };
187
+ request.localContext = await module.inspectEmployeeWorkspace(get, options.employee, agent, api, request);
215
188
  }
216
189
  sourceStage = 'login-readiness';
217
- const { target, connection } = await request.timing.run('login-ready', async () => {
190
+ const { target, connection } = await (async () => {
218
191
  request.localAdmissionCheck?.();
219
192
  const before = api.getConnectionState();
220
193
  const target = await ensureReady(api, agent.loginProvider, request.signal);
@@ -224,11 +197,9 @@ export function connectProvider(get, options = {}) {
224
197
  throw new NeedError('CONNECTION_LOST', '远程主机连接组件已更换', '当前员工连接');
225
198
  const connection = await waitForLogin(api, agent.loginProvider, before, get, request.signal);
226
199
  return { target, connection };
227
- });
200
+ })();
228
201
  if (request.identity && request.identity !== identity(target))
229
202
  throw new NeedError('SESSION_CONTEXT_MISMATCH', '员工主机与原会话不一致,请新建会话', '原员工主机');
230
- if (options.employee && request.localAcceptance)
231
- await (await import("./employee-provider.js")).validateEmployeeUserTurn(get, options.employee, api, request, connection);
232
203
  request.localAdmissionCheck?.();
233
204
  admissions.set(request, { api, target, connection });
234
205
  sourceStage = 'local-admission';
@@ -236,7 +207,6 @@ export function connectProvider(get, options = {}) {
236
207
  if (locations && (locations.phase === 'failed' || locations.phase === 'disposed') && locations.instanceId === connection.instanceId && locations.epoch === connection.epoch && locations.need)
237
208
  throw connectNeedError(locations.need);
238
209
  await options.local?.admit?.(request, api, connection, target);
239
- confirmedSource?.assertCurrent();
240
210
  }
241
211
  catch (error) {
242
212
  admissions.delete(request);
@@ -244,8 +214,6 @@ export function connectProvider(get, options = {}) {
244
214
  request.recordEarlyFailure = undefined;
245
215
  if (record)
246
216
  await record(error, sourceStage);
247
- else
248
- await confirmedSource?.recordFailure(error, sourceStage);
249
217
  throw error;
250
218
  }
251
219
  };
@@ -255,16 +223,13 @@ export function connectProvider(get, options = {}) {
255
223
  const admitted = admissions.get(request);
256
224
  admissions.delete(request);
257
225
  const { api, target, connection } = admitted;
258
- const stable = () => request.timing.run('connection-confirm', () => waitForLogin(api, agent.loginProvider, connection, get, request.signal));
259
- await stable();
260
- request.localAdmissionCheck?.();
261
226
  if (!request.sessionId)
262
227
  throw new NeedError('PROJECT_BINDING_REQUIRED', '我需要当前会话和已选择的工作区,请使用原生工作区入口', 'DSH会话与原生工作区');
263
- const host = await hostWorkspaceGuard(get, api, connection, target, request, options.workspaceSource?.workspace, agent.loginProvider);
228
+ const host = await hostWorkspaceGuard(get, api, connection, target, request, options.workspace, agent.loginProvider);
264
229
  try {
265
230
  if (!host && options.employee) {
266
231
  employee ??= import("./employee-provider.js").then(module => module.createEmployeeProvider(get, options, agent));
267
- const local = await (await employee)({ api, target, connection, request, stable: signal => request.timing.run('connection-confirm', () => waitForLogin(api, agent.loginProvider, connection, get, signal)) });
232
+ const local = await (await employee)({ api, target, connection, request });
268
233
  if (local) {
269
234
  local.loginRequired = () => offerLogin(api, request, get, agent);
270
235
  return local;
@@ -272,79 +237,33 @@ export function connectProvider(get, options = {}) {
272
237
  }
273
238
  if (!host && agent.loginProvider === 'claude' && options.local) {
274
239
  const runtime = await options.local(request, api, connection, target);
275
- if (runtime) {
276
- try {
277
- await stable();
278
- }
279
- catch (error) {
280
- await runtime.dispose?.();
281
- throw error;
282
- }
240
+ if (runtime)
283
241
  return runtime;
284
- }
285
242
  }
286
- if (!host) {
287
- await options.notice?.reconcile(request, api);
288
- await stable();
289
- }
290
- const resolved = await request.timing.run('workspace-resolve', async () => host ? host.resolve() : unwrap(await awaitL(() => api.resolveSessionProject(request.sessionId, { provider: agent.loginProvider, purpose: 'user-turn' }), request.signal)));
291
- await stable();
292
- host?.assertCurrent();
293
- if (!resolved || resolved.instanceId !== connection.instanceId || resolved.epoch !== connection.epoch)
294
- throw new NeedError('CONNECTION_LOST', '工作区解析回复已失效', '当前连接代次');
295
- validateTarget(resolved.target);
243
+ // A local source must have been handled by its employee provider above.
244
+ // Host execution requires an explicit host location; no name-based resolver.
245
+ if (!host)
246
+ throw new NeedError('WORKSPACE_LOCATION_REQUIRED', '请选择文件夹来源;改变来源后请新建会话。', '已选择的本机或主机文件夹');
247
+ const resolved = await host.resolve();
296
248
  const project = resolved.project;
297
- const source = resolved.source;
298
- if (resolved.sessionId !== request.sessionId || !resolved.workspaceId || !resolved.bindingId || !Number.isSafeInteger(resolved.bindingRevision) || resolved.bindingRevision < 1 || !sameTarget(target, resolved.target) || !project || (!host && (source !== undefined && source !== 'legacy-host' || typeof project.name !== 'string' || !project.name || project.name === '.' || project.name === '..' || /[\\/\x00-\x1f\x7f]/.test(project.name) || project.path !== resolved.target.projectRoot + '/' + project.name)))
299
- throw new NeedError('PROJECT_BINDING_INVALID', '工作区绑定与当前会话或可信主机不一致', 'L核验的原生工作区绑定');
300
249
  if (request.cwd && request.cwd !== project.path || request.bindingId && request.bindingId !== resolved.bindingId || request.workspaceId && request.workspaceId !== resolved.workspaceId)
301
250
  throw new NeedError(host ? 'PROJECT_BINDING_INVALID' : 'SESSION_CONTEXT_MISMATCH', host ? '文件来源已改,请新建会话继续;原会话历史仍可查看。' : '原会话的工作区绑定已改变,请新建会话', '原工作区绑定');
302
- const details = await request.timing.run('details-open', async () => options.details?.(request, api, connection, target));
251
+ const details = await options.details?.(request, api, connection, target);
303
252
  try {
304
- if (details)
305
- request.timing.bind(detail => details.record(detail));
306
- await stable();
307
- host?.assertCurrent();
308
- if (host)
309
- details?.record({ kind: 'session', severity: 'info', code: 'HOST_EXECUTION_VALIDATION_POLICY', summary: '主机目录核验不可关闭:每轮接纳时由L核验主机身份、登录与物理目录;之后消息、审批回复与检查点只核对内存中的连接、登录与位置,靠已订阅的L连接和位置事件撤销,不重复SSH。' });
310
- let workspaceNotice = undefined;
311
- const notice = !host && 'notice' in resolved ? resolved.notice : undefined;
312
- if (notice) {
313
- if (!options.notice)
314
- throw new NeedError('DEPENDENCY_REQUIRED', '我需要官方会话持久记录来确认工作区说明', '官方会话与持久化服务');
315
- workspaceNotice = await request.timing.run('workspace-notice', () => options.notice.deliver(notice, resolved, request, api));
316
- await stable();
317
- }
318
253
  if (!options.open)
319
254
  throw new NeedError('RUNTIME_REQUIRED', '我需要主机运行适配器', '主机broker适配');
320
- const observedBinding = host ? undefined : api.getWorkspaceBindingState?.();
321
- const validateContext = async (ignoreCancellation = false) => {
322
- const check = () => waitForLogin(api, agent.loginProvider, connection, get, ignoreCancellation ? undefined : request.signal);
323
- await check();
324
- if (observedBinding) {
325
- const current = api.getWorkspaceBindingState();
326
- if (current.instanceId !== observedBinding.instanceId || !current.items.some(item => item.bindingId === resolved.bindingId && item.workspaceId === resolved.workspaceId && item.status === 'ready'))
327
- throw new NeedError('SESSION_CONTEXT_MISMATCH', '当前工作区绑定已改变,原会话操作未执行', '原生会话原绑定');
328
- }
329
- await check();
330
- if (!ignoreCancellation)
331
- host?.assertCurrent();
332
- };
333
- host?.assertCurrent();
334
- const runtime = await options.open(resolved.target, project, { ...request, ...(host ? { signal: host.signal } : {}), agent, bindingId: resolved.bindingId, workspaceId: resolved.workspaceId, bindingRevision: resolved.bindingRevision, detailChannel: details, validateContext });
255
+ const runtime = await options.open(resolved.target, project, { ...request, ...(host ? { signal: host.signal } : {}), agent, bindingId: resolved.bindingId, workspaceId: resolved.workspaceId, bindingRevision: resolved.bindingRevision, detailChannel: details });
335
256
  try {
336
- await stable();
337
257
  host?.observeRuntime(runtime);
338
258
  }
339
259
  catch (error) {
340
- await runtime.dispose?.();
260
+ void runtime.dispose?.().catch(() => console.warn('HOST_CLEANUP_FAILED'));
341
261
  throw error;
342
262
  }
343
263
  if (host)
344
264
  retainHostScope(runtime, host);
345
265
  runtime.hostWorkspace = host && 'directory' in resolved ? Object.freeze({ cwd: resolved.directory.path }) : undefined;
346
266
  // The transport can outlive a turn; presentation belongs only to this invocation.
347
- runtime.workspaceNotice = workspaceNotice || undefined;
348
267
  runtime.details = details ?? runtime.details;
349
268
  runtime.binding = { bindingId: resolved.bindingId, workspaceId: resolved.workspaceId, revision: resolved.bindingRevision };
350
269
  runtime.connection = { ...connection };
@@ -352,13 +271,6 @@ export function connectProvider(get, options = {}) {
352
271
  return runtime;
353
272
  }
354
273
  catch (error) {
355
- try {
356
- await details?.flush();
357
- }
358
- catch (diagnostic) {
359
- const code = (value) => value instanceof NeedError && /^[A-Z][A-Z0-9_]*$/.test(value.code) ? value.code : 'UNKNOWN_FAILURE';
360
- throw new NeedError('CONVERSATION_DETAILS_FAILED', '技术详情未能保存;本轮已停止', `原错误代码 ${code(error)};诊断错误代码 ${code(diagnostic)}`);
361
- }
362
274
  throw error;
363
275
  }
364
276
  }
package/dist/readiness.js CHANGED
@@ -39,12 +39,6 @@ import { unwrap, awaitL, requireLoginCoordinator, requireLoginState } from "./pr
39
39
  import { NeedError } from "./remote.js";
40
40
  import { CONNECTION_REQUIRED, employeeFailure } from "./presentation.js";
41
41
  import { CODEX_AGENT } from "./agents.js";
42
- function details(target) {
43
- const h = target.hostContext;
44
- if (!h)
45
- return [];
46
- return [{ label: '主机配置', value: h.enforcement }, { label: '检查退出码', value: String(h.checkExitCode) }, ...h.failedChecks.map(item => ({ label: '未通过检查', value: `${item.id}(${item.status})` })), ...h.unverifiedChecks.map(item => ({ label: '未核实检查', value: `${item.id}(${item.status})` }))];
47
- }
48
42
  function blocked(code, technical = [], agent = CODEX_AGENT) {
49
43
  return { phase: 'blocked', loginStatus: 'unknown', title: `无法确认 ${agent.name.zh} 登录状态`, message: employeeFailure(code, agent.loginProvider), technical: [...technical, { label: '诊断代码', value: code }] };
50
44
  }
@@ -61,7 +55,7 @@ function snapshot(api, agent = CODEX_AGENT) {
61
55
  if (!connection.instanceId || state.instanceId !== connection.instanceId)
62
56
  return denied('AUTH_STATUS_INVALID', [diagnostic('LOGIN_INSTANCE_MISMATCH')]);
63
57
  requireLoginState(state);
64
- const technical = details({ hostContext: state.technicalDetails?.hostContext });
58
+ const technical = [];
65
59
  if (state.phase === 'blocked' || state.phase === 'disposed')
66
60
  return denied(state.technicalDetails?.code ?? 'AUTH_STATUS_INVALID', [...technical, diagnostic(state.phase === 'disposed' ? 'LOGIN_PHASE_DISPOSED' : 'LOGIN_PHASE_BLOCKED')]);
67
61
  if (state.queuedProviders?.includes(brand) || state.checkingProviders.includes(brand) || state.phase === 'authorizing' && state.provider === brand)
@@ -1,9 +1,30 @@
1
- import { type DetailObservation } from './conversation-details.ts';
1
+ import { type DetailObservation, type JsonValue } from './conversation-details.ts';
2
2
  import type { LoginDiagnostic } from './login-diagnostic.ts';
3
- import type { ConnectApi as LConnectApi, ConnectionState } from '@ai-workbench/connect/api';
4
- export type { RemoteConversationDetail as RemoteDetail } from '@ai-workbench/connect/api';
5
- type Connection = Pick<ConnectionState, 'instanceId' | 'epoch' | 'phase'>;
6
- export type RemoteDetailsApi = Pick<LConnectApi, 'getConnectionState'> & Partial<Pick<LConnectApi, 'getContractInfo' | 'appendRemoteConversationDetail' | 'getRemoteConversationDetailReceipt' | 'listRemoteConversationDetails' | 'appendLoginDiagnostic' | 'getLoginDiagnosticReceipt' | 'listLoginDiagnostics'>>;
3
+ type Connection = {
4
+ instanceId: string;
5
+ epoch: number;
6
+ phase: string;
7
+ };
8
+ export interface RemoteDetail {
9
+ version: 1;
10
+ eventId: string;
11
+ instanceId: string | null;
12
+ epoch: number | null;
13
+ sessionId: string;
14
+ provider: 'claude' | 'codex';
15
+ turn: string | number | null;
16
+ step: string | number | null;
17
+ sequence: number;
18
+ kind: string;
19
+ severity: 'info' | 'warning' | 'action-required';
20
+ code: string;
21
+ summary: string;
22
+ method?: string;
23
+ payload: JsonValue;
24
+ }
25
+ export interface RemoteDetailsApi {
26
+ getConnectionState(): Connection;
27
+ }
7
28
  export interface DetailChannel {
8
29
  record(observation: DetailObservation): void;
9
30
  beginTurn(ids: {
@@ -11,26 +32,31 @@ export interface DetailChannel {
11
32
  step: string | number | null;
12
33
  }): void;
13
34
  endTurn(): void;
14
- flush(): Promise<void>;
15
35
  }
16
36
  export interface DetailRequest {
17
37
  sessionId: string;
18
38
  provider: 'claude' | 'codex';
19
39
  identity: string;
20
- connection: Connection;
21
- readonly localAcceptanceId?: string;
40
+ connection?: Connection;
22
41
  }
23
42
  export declare function privateDirectory(path: string): Promise<void>;
24
- export declare function durableWrite(path: string, value: unknown): Promise<void>;
25
- export declare function readJournal(path: string): Promise<unknown>;
26
43
  export interface DiagnosticBinding {
27
44
  api: RemoteDetailsApi | undefined;
28
45
  connection: Connection | undefined;
29
46
  }
47
+ /** Diagnostics are best effort. No delivery journal, receipt lookup, replay or turn gate. */
30
48
  export declare function createRemoteDetails(options: {
31
- profileDir: string;
32
- getApi: () => RemoteDetailsApi | undefined;
49
+ profileDir?: string;
50
+ getApi?: () => unknown;
51
+ warn?: (line: string) => void;
33
52
  }): {
53
+ /** Await queued best-effort writes only when releasing the profile, never before dispatch. */
54
+ dispose(): Promise<void>;
55
+ releaseSession(sessionId: string): void;
56
+ read(query: {
57
+ sessionId: string;
58
+ provider: "claude" | "codex";
59
+ }): Promise<RemoteDetail[]>;
34
60
  recordLocalFailure(request: {
35
61
  sessionId?: string;
36
62
  provider: "claude" | "codex";
@@ -46,8 +72,7 @@ export declare function createRemoteDetails(options: {
46
72
  step: string | number | null;
47
73
  };
48
74
  }): Promise<{
49
- eventId: string;
50
- recorded: true;
75
+ eventId: `${string}-${string}-${string}-${string}-${string}`;
51
76
  }>;
52
77
  recordPreflightCheck(request: {
53
78
  sessionId?: string;
@@ -58,10 +83,9 @@ export declare function createRemoteDetails(options: {
58
83
  step: string | number | null;
59
84
  };
60
85
  binding?: DiagnosticBinding;
61
- assertScope?: () => void;
62
86
  }): Promise<{
63
- eventId: string;
64
- recorded: true;
87
+ eventId: `${string}-${string}-${string}-${string}-${string}`;
65
88
  }>;
66
89
  open(request: DetailRequest): Promise<DetailChannel>;
67
90
  };
91
+ export {};