@ours.network/fleet 1.0.3 → 1.0.5

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 (43) hide show
  1. package/README.md +13 -9
  2. package/dist/application/errors.d.ts +1 -1
  3. package/dist/application/role-command-service.d.ts +29 -1
  4. package/dist/application/role-command-service.js +41 -2
  5. package/dist/application/role-creation-service.d.ts +32 -1
  6. package/dist/application/role-creation-service.js +56 -10
  7. package/dist/application/role-removal-service.d.ts +19 -0
  8. package/dist/application/role-removal-service.js +13 -3
  9. package/dist/application/session-mutations.d.ts +7 -0
  10. package/dist/application/session-mutations.js +8 -0
  11. package/dist/application/task-room-service.d.ts +262 -0
  12. package/dist/application/task-room-service.js +571 -0
  13. package/dist/atomic-file.d.ts +5 -3
  14. package/dist/atomic-file.js +32 -8
  15. package/dist/build-info.json +4 -4
  16. package/dist/cli.js +39 -15
  17. package/dist/config.d.ts +6 -3
  18. package/dist/config.js +0 -17
  19. package/dist/docs.d.ts +1 -1
  20. package/dist/docs.js +26 -4
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/owner-channel/attachments.d.ts +2 -4
  24. package/dist/owner-channel/attachments.js +6 -39
  25. package/dist/owner-channel/channel.d.ts +5 -0
  26. package/dist/owner-channel/channel.js +159 -21
  27. package/dist/owner-channel/commands.d.ts +43 -1
  28. package/dist/owner-channel/commands.js +137 -130
  29. package/dist/rooms-tasks/cli.js +301 -463
  30. package/dist/rooms-tasks/task-lists.d.ts +19 -0
  31. package/dist/rooms-tasks/task-lists.js +96 -0
  32. package/dist/rooms-tasks/task-state.d.ts +3 -0
  33. package/dist/rooms-tasks/task-state.js +281 -175
  34. package/dist/rooms-tasks/types.d.ts +11 -1
  35. package/dist/runner.js +10 -33
  36. package/dist/session/control.d.ts +10 -1
  37. package/dist/session/control.js +22 -21
  38. package/dist/watchdog/query.d.ts +2 -0
  39. package/dist/watchdog/query.js +7 -3
  40. package/dist/web/runtime.js +2 -0
  41. package/dist/web/server.d.ts +2 -0
  42. package/dist/web/server.js +88 -3
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -400,7 +400,6 @@ roles:
400
400
  max_file_bytes: 10485760 # 10 MiB
401
401
  max_request_bytes: 20971520 # 20 MiB total, and >= max_file_bytes
402
402
  retention_ms: 86400000 # stale crash cleanup; 1 minute..30 days
403
- allowed_mime: [application/pdf, text/plain, image/png, audio/ogg]
404
403
  model: claude-fable-5 # launch on a specific model (pass-through id; default: launcher default)
405
404
  mission: one line
406
405
  persona: | # operating contract (published as persona)
@@ -967,20 +966,25 @@ message plaintext in fleet state.
967
966
  Inbound owner attachments use the same authenticated-CID and exact-wire routing
968
967
  boundary. Fleet first calls the metadata-only `listIncomingFiles`, groups a
969
968
  file-only wake or a same-sender reply-linked text caption, and checks the enabled,
970
- count, declared MIME, per-file size, and total-size policy before retrieving any
971
- bytes. It then calls selective `getFiles` only for the admitted wire IDs. An
969
+ count, per-file size, and total-size policy before retrieving any bytes. MIME values,
970
+ extensions, file categories, and declared-versus-detected mismatches are metadata,
971
+ never rejection criteria. It then calls selective `getFiles` only for the admitted wire IDs. An
972
972
  unauthorized sender is ignored without retrieval or reply. A rejected authorized
973
973
  request receives a bounded reason correlated to its file wire.
974
974
 
975
975
  Retrieved files must be regular, non-symlink paths whose byte count and SHA-256
976
- match typed SDK metadata. Fleet additionally checks content signatures against the
977
- declared MIME, sanitizes traversal/control characters from names, and copies each
976
+ match typed SDK metadata. Fleet detects recognizable content only to report metadata,
977
+ sanitizes traversal/control characters from names, and copies each
978
978
  file into a random request-scoped directory at mode 0700 with files at mode 0600.
979
979
  The `[fleet-owner]` turn receives only bounded metadata, the private local paths,
980
980
  and an explicit daemon transcription result for voice messages. Successful
981
981
  transcripts are included; `failed` and `unavailable` states are stated plainly so
982
982
  the agent must use the audio path rather than inventing text.
983
983
 
984
+ The legacy `attachments.allowed_mime` key is accepted and ignored so existing
985
+ configurations continue to load. It is omitted from resolved configuration and
986
+ cannot affect attachment admission.
987
+
984
988
  Request files are removed after final delivery and stale directories are removed
985
989
  after `retention_ms`. A bounded mode-0600 recovery journal stores only owner CID
986
990
  and wire routing metadata—never filenames, paths, captions, transcripts, or file
@@ -1002,11 +1006,11 @@ Managed-agent file egress is a separate trust direction. It retains the same
1002
1006
  metadata provenance checks, count/per-file/request byte caps, regular-file and
1003
1007
  no-symlink reads, actual byte-count/SHA-256 verification, filename sanitization,
1004
1008
  private staging, fixed owner routing, and bounded body-free recovery metadata.
1005
- It deliberately does not consult `attachments.enabled` or `allowed_mime`; MIME is
1009
+ It deliberately does not consult `attachments.enabled`; MIME is
1006
1010
  still detected during byte validation, but a declared-versus-detected mismatch is
1007
- not an egress deny gate. Markdown, HTML, octet-stream, unknown extensions, and normal
1008
- binaries are therefore routable only from the exact configured `agent` CID.
1009
- Owner-to-agent admission above remains byte-for-byte strict. Each outbound file
1011
+ not a deny gate in either direction. Markdown, HTML, octet-stream, unknown extensions,
1012
+ and normal binaries are therefore routable only across their authenticated direction.
1013
+ Each outbound file
1010
1014
  gets a durable pre-send marker: a failed/ambiguous send becomes `uncertain` and is
1011
1015
  not blindly retried, while files not yet attempted remain recoverable after restart.
1012
1016
  Logs contain counts and byte totals, never filenames or raw bytes.
@@ -1,4 +1,4 @@
1
- export type FleetErrorCode = 'role_not_found' | 'capability_unavailable' | 'invalid_request' | 'offline' | 'control_unavailable' | 'timeout' | 'rejected' | 'backend_failure' | 'conflict' | 'stale_state' | 'prerequisite_unavailable' | 'idempotency_conflict' | 'rollback_incomplete' | 'launched_unconfirmed' | 'unauthorized' | 'forbidden' | 'rate_limited' | 'incompatible_version' | 'internal';
1
+ export type FleetErrorCode = 'role_not_found' | 'resource_not_found' | 'capability_unavailable' | 'invalid_request' | 'offline' | 'control_unavailable' | 'timeout' | 'rejected' | 'backend_failure' | 'conflict' | 'stale_state' | 'prerequisite_unavailable' | 'idempotency_conflict' | 'rollback_incomplete' | 'launched_unconfirmed' | 'unauthorized' | 'forbidden' | 'rate_limited' | 'incompatible_version' | 'internal';
2
2
  export interface FleetErrorShape {
3
3
  code: FleetErrorCode;
4
4
  message: string;
@@ -1,7 +1,32 @@
1
- import { type OpsDeps } from '../ops.js';
1
+ import { loadConfig } from '../config.js';
2
+ import { restartRoles, type OpsDeps } from '../ops.js';
2
3
  import { FleetError } from './errors.js';
3
4
  import { RoleRepository } from './role-repository.js';
4
5
  import type { RoleStatus } from './types.js';
6
+ export interface RestartBatchPlan {
7
+ readonly roleIds: readonly string[];
8
+ readonly mode: 'keep' | 'fresh';
9
+ readonly config: ReturnType<typeof loadConfig>;
10
+ readonly configPath?: string;
11
+ }
12
+ /** Shared restart kernel. Receipt policy and surface rendering stay with callers. */
13
+ export declare class RoleLifecycleService {
14
+ private readonly options;
15
+ constructor(options: RoleCommandOptions);
16
+ prepareRestart(input: {
17
+ roleIds: string[];
18
+ mode: 'keep' | 'fresh';
19
+ config?: ReturnType<typeof loadConfig>;
20
+ }): Promise<RestartBatchPlan>;
21
+ executeRestart(plan: RestartBatchPlan): Promise<void>;
22
+ status(roleId: string): Promise<RoleStatus>;
23
+ }
24
+ /** Adapter-facing batch entry point used by CLI and detached Messenger workers. */
25
+ export declare function executeRestartBatch(lifecycle: Pick<RoleLifecycleService, 'prepareRestart' | 'executeRestart'>, input: {
26
+ roleIds: string[];
27
+ mode: 'keep' | 'fresh';
28
+ config?: ReturnType<typeof loadConfig>;
29
+ }): Promise<RestartBatchPlan>;
5
30
  export type LifecycleAction = 'start' | 'stop' | 'restart_resume' | 'restart_fresh';
6
31
  export interface CommandReceipt {
7
32
  actionId: string;
@@ -19,11 +44,14 @@ export interface RoleCommandOptions {
19
44
  configPath?: string;
20
45
  status(roleId: string): Promise<RoleStatus>;
21
46
  onProgress?: (receipt: CommandReceipt) => void;
47
+ restart?: typeof restartRoles;
48
+ lifecycle?: RoleLifecycleService;
22
49
  }
23
50
  export declare class RoleCommandService {
24
51
  private readonly options;
25
52
  private readonly receipts;
26
53
  private readonly locks;
54
+ readonly lifecycle: RoleLifecycleService;
27
55
  constructor(options: RoleCommandOptions);
28
56
  execute(input: {
29
57
  roleId: string;
@@ -2,12 +2,46 @@ import { randomUUID } from 'node:crypto';
2
2
  import { loadConfig } from '../config.js';
3
3
  import { up, restartRoles } from '../ops.js';
4
4
  import { FleetError, normalizeError } from './errors.js';
5
+ /** Shared restart kernel. Receipt policy and surface rendering stay with callers. */
6
+ export class RoleLifecycleService {
7
+ options;
8
+ constructor(options) {
9
+ this.options = options;
10
+ }
11
+ async prepareRestart(input) {
12
+ const config = input.config ?? loadConfig(this.options.configPath);
13
+ const selected = input.roleIds.length ? input.roleIds : config.roles.map(role => role.name);
14
+ for (const roleId of selected) {
15
+ const role = await this.options.repository.get(roleId);
16
+ if (!role)
17
+ throw new FleetError('role_not_found', `no such role '${roleId}'`);
18
+ if (role.lifetime !== 'permanent')
19
+ throw new FleetError('capability_unavailable', 'lifecycle is unavailable for temporary/orphan roles');
20
+ }
21
+ return Object.freeze({
22
+ roleIds: Object.freeze([...input.roleIds]), mode: input.mode, config,
23
+ ...(this.options.configPath ? { configPath: this.options.configPath } : {}),
24
+ });
25
+ }
26
+ async executeRestart(plan) {
27
+ await (this.options.restart ?? restartRoles)(plan.config, [...plan.roleIds], this.options.ops, plan.mode, plan.configPath);
28
+ }
29
+ status(roleId) { return this.options.status(roleId); }
30
+ }
31
+ /** Adapter-facing batch entry point used by CLI and detached Messenger workers. */
32
+ export async function executeRestartBatch(lifecycle, input) {
33
+ const plan = await lifecycle.prepareRestart(input);
34
+ await lifecycle.executeRestart(plan);
35
+ return plan;
36
+ }
5
37
  export class RoleCommandService {
6
38
  options;
7
39
  receipts = new Map();
8
40
  locks = new Map();
41
+ lifecycle;
9
42
  constructor(options) {
10
43
  this.options = options;
44
+ this.lifecycle = options.lifecycle ?? new RoleLifecycleService(options);
11
45
  }
12
46
  async execute(input) {
13
47
  const actionId = input.actionId ?? randomUUID();
@@ -42,8 +76,13 @@ export class RoleCommandService {
42
76
  await up(config, [receipt.roleId], this.options.ops, this.options.configPath);
43
77
  else if (receipt.action === 'stop')
44
78
  await this.options.ops.backend.stop(receipt.roleId);
45
- else
46
- await restartRoles(config, [receipt.roleId], this.options.ops, receipt.action === 'restart_fresh' ? 'fresh' : 'keep', this.options.configPath);
79
+ else {
80
+ const plan = await this.lifecycle.prepareRestart({
81
+ roleIds: [receipt.roleId],
82
+ mode: receipt.action === 'restart_fresh' ? 'fresh' : 'keep', config,
83
+ });
84
+ await this.lifecycle.executeRestart(plan);
85
+ }
47
86
  try {
48
87
  const status = await this.options.status(receipt.roleId);
49
88
  receipt.postcondition = { overall: status.overall, observedAt: status.observedAt };
@@ -1,8 +1,10 @@
1
1
  import { type CommonPermissions, type MonitorConfig, type MonitorInterrupt, type NotifyEventType } from '../config.js';
2
2
  import { type IdentityProvisioner } from '../creation.js';
3
- import { type SupervisorLauncher } from '../spawn.js';
3
+ import { spawnDryRun, type SpawnOpts, type SupervisorLauncher } from '../spawn.js';
4
4
  import type { OpsDeps } from '../ops.js';
5
+ import type { ResolvedRole } from '../config.js';
5
6
  import { FleetError } from './errors.js';
7
+ import { type ManagedFleetSpawnResult } from '../fleet-proxy.js';
6
8
  import { type HarnessModelCatalog, type HarnessModelOption } from './model-catalog.js';
7
9
  export interface CreateRoleSessionRequest {
8
10
  name: string;
@@ -122,7 +124,20 @@ export interface RoleCreationServiceOptions {
122
124
  probeReady?: (name: string, session: 'acp' | 'tmux') => Promise<'ready' | 'attention' | 'unknown'>;
123
125
  onProgress?: (action: CreationAction) => void;
124
126
  modelCatalogs?: Partial<Record<'codex' | 'claude-code', () => HarnessModelCatalog>>;
127
+ /** Direct/managed callers must not create or restore the web action journal. */
128
+ journal?: boolean;
125
129
  }
130
+ export type CreationPlan = {
131
+ origin: 'direct';
132
+ options: SpawnOpts;
133
+ preview: ReturnType<typeof spawnDryRun>;
134
+ } | {
135
+ origin: 'managed';
136
+ options: SpawnOpts;
137
+ preview: ReturnType<typeof spawnDryRun>;
138
+ caller: string;
139
+ inherited: string[];
140
+ };
126
141
  export declare class RoleCreationService {
127
142
  private readonly options;
128
143
  private readonly actions;
@@ -131,6 +146,22 @@ export declare class RoleCreationService {
131
146
  private readonly journalDir;
132
147
  private readonly identityProvisioner;
133
148
  constructor(options: RoleCreationServiceOptions);
149
+ previewSpawn(input: {
150
+ origin: 'direct';
151
+ options: SpawnOpts;
152
+ } | {
153
+ origin: 'managed';
154
+ caller: ResolvedRole;
155
+ options: SpawnOpts;
156
+ }): CreationPlan;
157
+ createDirect(options: SpawnOpts): Promise<{
158
+ plan: Extract<CreationPlan, {
159
+ origin: 'direct';
160
+ }>;
161
+ statePath: string;
162
+ }>;
163
+ createManaged(caller: ResolvedRole, requested: SpawnOpts): Promise<ManagedFleetSpawnResult>;
164
+ private launchSync;
134
165
  capabilities(): Promise<CreationCapabilities>;
135
166
  preview(input: CreateRoleSessionRequest): Promise<CreationPreview>;
136
167
  create(input: CreateRoleSessionRequest, previewHash: string, idempotencyKey: string, browserSession: string): Promise<CreationAction>;
@@ -5,8 +5,11 @@ import { loadConfig, NOTIFY_EVENT_TYPES, resolveMonitorConfig, resolveRoleModel,
5
5
  import { daemonIdentityProvisioner, } from '../creation.js';
6
6
  import { replaceFileAtomically } from '../atomic-file.js';
7
7
  import { stateRoot } from '../paths.js';
8
- import { buildRoleConfig, spawnPermanent, spawnTemp, validateSpawnOpts, } from '../spawn.js';
8
+ import { buildRoleConfig, spawnDryRun, spawnPermanent, spawnTemp, validateSpawnOpts, } from '../spawn.js';
9
9
  import { FleetError, normalizeError } from './errors.js';
10
+ import { inheritCallerSpawnDefaults } from '../fleet-proxy.js';
11
+ import { effectivePermissionMode } from '../permissions.js';
12
+ import { effectiveRoleModel } from '../model-env.js';
10
13
  import { claudeModelCatalog, codexModelCatalog, } from './model-catalog.js';
11
14
  const canonical = (value) => {
12
15
  if (Array.isArray(value))
@@ -33,9 +36,46 @@ export class RoleCreationService {
33
36
  constructor(options) {
34
37
  this.options = options;
35
38
  this.journalDir = options.journalDir ?? `${stateRoot()}/web/creation-actions`;
36
- mkdirSync(this.journalDir, { recursive: true, mode: 0o700 });
39
+ if (options.journal !== false)
40
+ mkdirSync(this.journalDir, { recursive: true, mode: 0o700 });
37
41
  this.identityProvisioner = options.identityProvisioner ?? daemonIdentityProvisioner();
38
- this.restore();
42
+ if (options.journal !== false)
43
+ this.restore();
44
+ }
45
+ previewSpawn(input) {
46
+ if (input.origin === 'direct') {
47
+ const options = { ...input.options, surface: 'cli' };
48
+ return { origin: 'direct', options, preview: spawnDryRun(options) };
49
+ }
50
+ const inherited = inheritCallerSpawnDefaults(input.caller, input.options, this.options.configPath);
51
+ const options = { ...inherited.options, surface: 'agent',
52
+ callerRole: input.caller.name };
53
+ return { origin: 'managed', options, caller: input.caller.name,
54
+ inherited: inherited.inherited, preview: spawnDryRun(options) };
55
+ }
56
+ async createDirect(options) {
57
+ const plan = this.previewSpawn({ origin: 'direct', options });
58
+ return { plan, statePath: await this.launchSync(plan.options) };
59
+ }
60
+ async createManaged(caller, requested) {
61
+ const plan = this.previewSpawn({ origin: 'managed', caller, options: requested });
62
+ const creationActionId = randomUUID();
63
+ plan.options.creationActionId = creationActionId;
64
+ const statePath = await this.launchSync(plan.options);
65
+ return {
66
+ caller: plan.caller, role: plan.options.name,
67
+ lifetime: plan.options.temp ? 'temporary' : 'permanent', statePath,
68
+ harness: plan.preview.resolvedRole.harness, session: plan.preview.resolvedRole.session,
69
+ ...(effectiveRoleModel(plan.preview.resolvedRole) ? { model: effectiveRoleModel(plan.preview.resolvedRole) } : {}),
70
+ monitor: { mode: plan.preview.resolvedRole.monitor.mode, interrupt: plan.preview.resolvedRole.monitor.interrupt },
71
+ permissionMode: effectivePermissionMode(plan.preview.resolvedRole), inherited: plan.inherited,
72
+ creationActionId,
73
+ };
74
+ }
75
+ launchSync(options, creation) {
76
+ return options.temp
77
+ ? spawnTemp(options, this.options.binPath, this.options.tempLauncher, creation)
78
+ : spawnPermanent(options, this.options.ops, creation);
39
79
  }
40
80
  async capabilities() {
41
81
  const reasons = [];
@@ -216,12 +256,7 @@ export class RoleCreationService {
216
256
  : { exists: name => this.identityProvisioner.exists(name) },
217
257
  onStage: (stage, evidence) => this.coreStage(action, stage, evidence),
218
258
  };
219
- if (preview.effective.lifetime === 'permanent') {
220
- await spawnPermanent(this.spawnOptions(preview.request, action.actionId), this.options.ops, creation);
221
- }
222
- else {
223
- await spawnTemp(this.spawnOptions(preview.request, action.actionId), this.options.binPath, this.options.tempLauncher, creation);
224
- }
259
+ await this.launchSync(this.spawnOptions(preview.request, action.actionId), creation);
225
260
  this.stage(action, 'launched', 'launch accepted; readiness not yet confirmed');
226
261
  this.stage(action, 'identity_bootstrap_pending', preview.effective.lifetime === 'permanent'
227
262
  ? 'fleet established the permanent identity; the harness must bind it from the generated briefing'
@@ -262,6 +297,16 @@ export class RoleCreationService {
262
297
  return 'unknown';
263
298
  }
264
299
  validate(input) {
300
+ const allowed = new Set([
301
+ 'name', 'harness', 'model', 'reasoningEffort', 'session', 'cwd', 'lifetime', 'mission',
302
+ 'coordinator', 'permissions', 'bio', 'persona', 'monitor', 'openAfterCreate',
303
+ 'highRiskAcknowledged', 'reuseExistingIdentityAcknowledged',
304
+ 'unverifiedIdentityAcknowledged',
305
+ ]);
306
+ const unsupported = Object.keys(input)
307
+ .filter(key => !allowed.has(key));
308
+ if (unsupported.length)
309
+ throw new FleetError('invalid_request', `unsupported web creation field: ${unsupported[0]}`);
265
310
  if (!ROLE_NAME_RE.test(input.name))
266
311
  throw new FleetError('invalid_request', 'invalid role name');
267
312
  if (!['codex', 'claude-code'].includes(input.harness))
@@ -316,7 +361,8 @@ export class RoleCreationService {
316
361
  }
317
362
  spawnOptions(request, creationActionId) {
318
363
  return {
319
- name: request.name, identity: request.name, harness: request.harness,
364
+ name: request.name, temp: request.lifetime === 'temporary',
365
+ identity: request.name, harness: request.harness,
320
366
  model: request.model, session: request.session, cwd: request.cwd,
321
367
  reasoningEffort: request.reasoningEffort,
322
368
  mission: request.mission, coordinator: request.coordinator,
@@ -19,7 +19,26 @@ export declare class RoleRemovalService {
19
19
  ops: OpsDeps;
20
20
  currentControlRole?: string;
21
21
  });
22
+ removeDirect(input: {
23
+ actor: {
24
+ kind: 'local_control';
25
+ surface: 'cli';
26
+ };
27
+ role: string;
28
+ }): Promise<void>;
29
+ previewWeb(requested: string): RoleRemovalPreview;
30
+ removeWeb(input: {
31
+ role: string;
32
+ confirmation?: string;
33
+ confirmed?: boolean;
34
+ coordinatorAcknowledged?: boolean;
35
+ }): Promise<RoleRemovalPreview & {
36
+ removed: true;
37
+ recoveryPath: string;
38
+ }>;
39
+ /** @deprecated Internal compatibility aliases; surface adapters use explicit policies above. */
22
40
  preview(requested: string): RoleRemovalPreview;
41
+ /** @deprecated Internal compatibility aliases; surface adapters use explicit policies above. */
23
42
  remove(input: {
24
43
  role: string;
25
44
  confirmation?: string;
@@ -9,7 +9,11 @@ export class RoleRemovalService {
9
9
  constructor(options) {
10
10
  this.options = options;
11
11
  }
12
- preview(requested) {
12
+ async removeDirect(input) {
13
+ const cfg = loadConfig(this.options.configPath);
14
+ await rmRole(cfg, input.role, this.options.ops);
15
+ }
16
+ previewWeb(requested) {
13
17
  if (!ROLE_NAME_RE.test(requested))
14
18
  throw new FleetError('invalid_request', 'invalid role name');
15
19
  const cfg = loadConfig(this.options.configPath);
@@ -54,8 +58,8 @@ export class RoleRemovalService {
54
58
  recovery: { available: true, detail: 'State and spawned configuration are copied to the local removal archive before deletion.' },
55
59
  };
56
60
  }
57
- async remove(input) {
58
- const preview = this.preview(input.role);
61
+ async removeWeb(input) {
62
+ const preview = this.previewWeb(input.role);
59
63
  if (preview.selfProtected)
60
64
  throw new FleetError('forbidden', 'the current control role cannot remove itself from its own web session');
61
65
  if (preview.coordinatorProtection && !input.coordinatorAcknowledged)
@@ -84,4 +88,10 @@ export class RoleRemovalService {
84
88
  }
85
89
  return { ...preview, removed: true, recoveryPath };
86
90
  }
91
+ /** @deprecated Internal compatibility aliases; surface adapters use explicit policies above. */
92
+ preview(requested) { return this.previewWeb(requested); }
93
+ /** @deprecated Internal compatibility aliases; surface adapters use explicit policies above. */
94
+ remove(input) {
95
+ return this.removeWeb(input);
96
+ }
87
97
  }
@@ -0,0 +1,7 @@
1
+ import type { InterruptOutcome, QueuedPrompt, SessionHandle, SubmitPromptOptions, TurnCancellationSource } from '../session/types.js';
2
+ /** Thin in-process session mutation kernel. Callers retain every policy decision. */
3
+ export declare const queueSessionPrompt: (session: SessionHandle, text: string, options?: SubmitPromptOptions) => Promise<QueuedPrompt>;
4
+ export declare const interruptSession: (session: SessionHandle, source: TurnCancellationSource) => Promise<InterruptOutcome>;
5
+ export declare const respondSessionPermission: (session: SessionHandle, permissionId: string, optionId: string) => boolean;
6
+ export type GenerationPermissionResult = 'accepted' | 'stale' | 'unavailable';
7
+ export declare const respondSessionPermissionV2: (session: SessionHandle, permissionId: string, optionId: string, sessionGeneration: string) => GenerationPermissionResult;
@@ -0,0 +1,8 @@
1
+ import { interruptOutcome } from '../session/types.js';
2
+ /** Thin in-process session mutation kernel. Callers retain every policy decision. */
3
+ export const queueSessionPrompt = (session, text, options) => session.queuePrompt(text, options);
4
+ export const interruptSession = async (session, source) => interruptOutcome(await session.interrupt(source));
5
+ export const respondSessionPermission = (session, permissionId, optionId) => session.respondPermission(permissionId, optionId);
6
+ export const respondSessionPermissionV2 = (session, permissionId, optionId, sessionGeneration) => session.respondPermissionV2
7
+ ? session.respondPermissionV2(permissionId, optionId, sessionGeneration)
8
+ : 'unavailable';