@demicodes/agent 0.5.0 → 0.6.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.
@@ -1,4 +1,4 @@
1
- import { d as TranscriptPatch, l as ServerFrame, n as AgentServerTransport, o as ClientFrame, r as AgentTransport, s as ClientSessionEvent, t as AgentClientTransport, u as ShellCommandStatusLike, y as AgentMetadata } from "./transport-qMjHM60a.mjs";
2
- import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-Bf-hWn0D.mjs";
1
+ import { b as AgentMetadata, d as TranscriptPatch, l as ServerFrame, n as AgentServerTransport, o as ClientFrame, r as AgentTransport, s as ClientSessionEvent, t as AgentClientTransport, u as ShellCommandStatusLike } from "./transport-bAa-oKZZ.mjs";
2
+ import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-mG7mSLtZ.mjs";
3
3
  import { ProviderSelection } from "@demicodes/provider";
4
4
  export { type AgentActionOptions, AgentClient, type AgentClientListener, type AgentClientTransport, type AgentMetadata, type AgentServerTransport, type AgentTransport, type ClientFrame, type ClientSessionEvent, type JsonWebSocket, type ProviderSelection, type ServerFrame, type ShellCommandStatusLike, type TranscriptPatch, createWebSocketClientTransport, createWebSocketServerTransport };
@@ -1,2 +1,2 @@
1
- import { n as createWebSocketServerTransport, r as AgentClient, t as createWebSocketClientTransport } from "./websocket-transport-DajIApki.mjs";
1
+ import { n as createWebSocketServerTransport, r as AgentClient, t as createWebSocketClientTransport } from "./websocket-transport-DGk5U5EK.mjs";
2
2
  export { AgentClient, createWebSocketClientTransport, createWebSocketServerTransport };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AgentToolInvokeContext, B as DEFAULT_TURN_RETRY_POLICY, C as AgentSessionOptions, D as AgentSystemPromptContext, E as AgentSessionStore, F as CompactionWindow, H as isRetryableCode, I as DrainedTranscriptPatches, L as TranscriptLog, M as ExternalMutationReservation, N as SessionEvent, O as AgentTool, P as SessionEventListener, R as TranscriptOptions, S as AgentSessionCheckpoint, T as AgentSessionRestoreParams, U as resolveRetryPolicy, V as TurnRetryPolicy, W as retryDelayMs, _ as AgentHarnessRuntime, a as createInProcessTransportPair, b as AgentPromptContext, c as ConversationSummary, d as TranscriptPatch, f as AbortResult, g as AgentHarnessContext, h as AgentHarness, i as InProcessTransportPair, j as AgentToolInvokeResult, k as AgentToolContext, l as ServerFrame, m as AgentDisposeContext, n as AgentServerTransport, o as ClientFrame, p as AbortTarget, r as AgentTransport, s as ClientSessionEvent, t as AgentClientTransport, u as ShellCommandStatusLike, v as AgentLifecycleEvent, w as AgentSessionParams, x as AgentReferenceResolveContext, y as AgentMetadata, z as estimateTranscriptBlockTokens } from "./transport-qMjHM60a.mjs";
2
- import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-Bf-hWn0D.mjs";
1
+ import { A as AgentToolContext, B as estimateTranscriptBlockTokens, C as AgentSessionCheckpoint, D as AgentSessionStore, E as AgentSessionRestoreParams, F as SessionEventListener, G as retryDelayMs, H as TurnRetryPolicy, I as CompactionWindow, L as DrainedTranscriptPatches, M as AgentToolInvokeResult, N as ExternalMutationReservation, O as AgentSystemPromptContext, P as SessionEvent, R as TranscriptLog, S as AgentReferenceResolveContext, T as AgentSessionParams, U as isRetryableCode, V as DEFAULT_TURN_RETRY_POLICY, W as resolveRetryPolicy, _ as AgentHarnessRuntime, a as createInProcessTransportPair, b as AgentMetadata, c as ConversationSummary, d as TranscriptPatch, f as AbortResult, g as AgentHarnessContext, h as AgentHarness, i as InProcessTransportPair, j as AgentToolInvokeContext, k as AgentTool, l as ServerFrame, m as AgentDisposeContext, n as AgentServerTransport, o as ClientFrame, p as AbortTarget, r as AgentTransport, s as ClientSessionEvent, t as AgentClientTransport, u as ShellCommandStatusLike, v as AgentHostContext, w as AgentSessionOptions, x as AgentPromptContext, y as AgentLifecycleEvent, z as TranscriptOptions } from "./transport-bAa-oKZZ.mjs";
2
+ import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-mG7mSLtZ.mjs";
3
3
  import { BashAuditEvent, BashEnvironment, BashEnvironmentOptions, CommandMetadataRecord, Host, ShellCommandStatus, ShellOutputChunk } from "@demicodes/shell";
4
4
  import { AgentProvider, Provider } from "@demicodes/provider";
5
5
  import { Block, Model, ModelSelection, QueuedMessage, SessionPhase, UserContentBlock } from "@demicodes/core";
@@ -172,18 +172,18 @@ interface AgentServerSessionOptions {
172
172
  persistIntervalMs?: number;
173
173
  }
174
174
  /**
175
- * Host-agnostic hook invoked once per `open()` before BashEnvironment is built.
175
+ * Host-agnostic hook invoked before each resolved Host's BashEnvironment is built.
176
176
  * LocalHost uses this to inject PATH / env for the command bridge; AgentServer
177
177
  * itself does not know about UDS, shims, or bin directories.
178
178
  */
179
- interface PrepareSessionShellContext {
179
+ interface PrepareShellContext {
180
180
  agentSessionId: string;
181
181
  host: Host;
182
182
  commandNames: readonly string[];
183
183
  /** Shell options from AgentServer construction (before this hook). */
184
184
  shell: Omit<BashEnvironmentOptions, 'host' | 'commands'>;
185
185
  }
186
- type PrepareSessionShell = (ctx: PrepareSessionShellContext) => Omit<BashEnvironmentOptions, 'host' | 'commands'> | Promise<Omit<BashEnvironmentOptions, 'host' | 'commands'>>;
186
+ type PrepareShell = (ctx: PrepareShellContext) => Omit<BashEnvironmentOptions, 'host' | 'commands'> | Promise<Omit<BashEnvironmentOptions, 'host' | 'commands'>>;
187
187
  interface AgentServerOptions {
188
188
  agent: AgentHarness<unknown>;
189
189
  providers: Provider[];
@@ -193,7 +193,7 @@ interface AgentServerOptions {
193
193
  * Optional host-side shell prep (env, PATH, etc.). Implementation-agnostic —
194
194
  * command bridge wiring lives in `@demicodes/host-local`, not here.
195
195
  */
196
- prepareSessionShell?: PrepareSessionShell;
196
+ prepareShell?: PrepareShell;
197
197
  }
198
198
  interface AgentTransportBinding {
199
199
  close(): Promise<void>;
@@ -212,9 +212,9 @@ interface RunCommandLineResult {
212
212
  stdoutEncoding?: 'base64';
213
213
  stderr: string;
214
214
  }
215
- declare class RunCommandLineSessionNotFoundError extends Error {
216
- readonly agentSessionId: string;
217
- constructor(agentSessionId: string);
215
+ declare class RunCommandLineShellNotFoundError extends Error {
216
+ readonly shellId: string;
217
+ constructor(shellId: string);
218
218
  }
219
219
  declare class RunCommandLineCommandNotRegisteredError extends Error {
220
220
  readonly commandName: string;
@@ -231,7 +231,7 @@ declare class AgentServer {
231
231
  private readonly providers;
232
232
  private readonly shellOptions;
233
233
  private readonly sessionOptions;
234
- private readonly prepareSessionShell;
234
+ private readonly prepareShell;
235
235
  private readonly bindings;
236
236
  private readonly sessionOwnership;
237
237
  constructor(options: AgentServerOptions);
@@ -243,12 +243,15 @@ declare class AgentServer {
243
243
  * Transport-agnostic: callers (e.g. LocalHost command bridge) supply their
244
244
  * own IPC; AgentServer only knows how to exec against the open session shell.
245
245
  */
246
- runCommandLine(agentSessionId: string, name: string, args: string[], opts: RunCommandLineOptions): Promise<RunCommandLineResult>;
246
+ runCommandLine(shellId: string, name: string, args: string[], opts: RunCommandLineOptions): Promise<RunCommandLineResult>;
247
247
  }
248
248
  //#endregion
249
249
  //#region src/tools.d.ts
250
250
  interface StandardAgentToolOptions<State = unknown> {
251
- environment: BashEnvironment;
251
+ environment: BashEnvironment | ((ctx: AgentToolInvokeContext<State>, handle: {
252
+ shellId?: string;
253
+ commandId?: string;
254
+ }) => BashEnvironment | Promise<BashEnvironment>);
252
255
  scheduleYield(ctx: AgentToolInvokeContext<State>, durationMs: number): AgentToolInvokeResult;
253
256
  }
254
257
  declare function createStandardAgentTools<State = unknown>(options: StandardAgentToolOptions<State>): AgentTool<State>[];
@@ -287,4 +290,4 @@ interface ShellToolView {
287
290
  declare function finishShellToolResult<State>(environment: BashEnvironment, result: ShellCommandStatus, ctx: AgentToolInvokeContext<State>): Promise<AgentToolInvokeResult>;
288
291
  declare function shellCommandHandleRequired(result: ShellCommandStatus, budgetTokens: number): boolean;
289
292
  //#endregion
290
- export { AbortResult, AbortTarget, ActiveTurnPhase, AgentActionOptions, AgentClient, AgentClientListener, AgentClientTransport, AgentDisposeContext, AgentHarness, AgentHarnessContext, AgentHarnessRuntime, AgentLifecycleEvent, AgentMetadata, AgentPromptContext, AgentReferenceResolveContext, AgentServer, AgentServerOptions, AgentServerSessionOptions, AgentServerTransport, AgentSession, AgentSessionCheckpoint, AgentSessionOptions, AgentSessionParams, AgentSessionRestoreParams, AgentSessionStore, AgentSystemPromptContext, AgentTool, AgentToolContext, AgentToolInvokeContext, AgentToolInvokeResult, AgentTransport, AgentTransportBinding, ClientFrame, ClientSessionEvent, CompactionWindow, ConversationSummary, DEFAULT_TURN_RETRY_POLICY, DrainedTranscriptPatches, ExternalMutationReservation, InProcessTransportPair, JsonWebSocket, PrepareSessionShell, PrepareSessionShellContext, RunCommandLineCommandNotRegisteredError, RunCommandLineOptions, RunCommandLineResult, RunCommandLineSessionNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, ServerFrame, SessionEvent, SessionEventListener, ShellCommandStatusLike, ShellToolResultOptions, ShellToolView, StandardAgentToolOptions, TranscriptLog, TranscriptOptions, TranscriptPatch, TurnRetryPolicy, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, finishShellToolResult, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
293
+ export { AbortResult, AbortTarget, ActiveTurnPhase, AgentActionOptions, AgentClient, AgentClientListener, AgentClientTransport, AgentDisposeContext, AgentHarness, AgentHarnessContext, AgentHarnessRuntime, AgentHostContext, AgentLifecycleEvent, AgentMetadata, AgentPromptContext, AgentReferenceResolveContext, AgentServer, AgentServerOptions, AgentServerSessionOptions, AgentServerTransport, AgentSession, AgentSessionCheckpoint, AgentSessionOptions, AgentSessionParams, AgentSessionRestoreParams, AgentSessionStore, AgentSystemPromptContext, AgentTool, AgentToolContext, AgentToolInvokeContext, AgentToolInvokeResult, AgentTransport, AgentTransportBinding, ClientFrame, ClientSessionEvent, CompactionWindow, ConversationSummary, DEFAULT_TURN_RETRY_POLICY, DrainedTranscriptPatches, ExternalMutationReservation, InProcessTransportPair, JsonWebSocket, PrepareShell, PrepareShellContext, RunCommandLineCommandNotRegisteredError, RunCommandLineOptions, RunCommandLineResult, RunCommandLineShellNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, ServerFrame, SessionEvent, SessionEventListener, ShellCommandStatusLike, ShellToolResultOptions, ShellToolView, StandardAgentToolOptions, TranscriptLog, TranscriptOptions, TranscriptPatch, TurnRetryPolicy, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, finishShellToolResult, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as cloneBlocks, i as applyTranscriptPatches, n as createWebSocketServerTransport, r as AgentClient, t as createWebSocketClientTransport } from "./websocket-transport-DajIApki.mjs";
1
+ import { a as cloneBlocks, i as applyTranscriptPatches, n as createWebSocketServerTransport, r as AgentClient, t as createWebSocketClientTransport } from "./websocket-transport-DGk5U5EK.mjs";
2
2
  import { AbortError, abortable, asError, asRecord, asString, bytesToBase64, createId, delay, isAbortError, isRecord, noop, parseJsonOrString, safeJsonStringify, throwIfAborted, truncate } from "@demicodes/utils";
3
3
  import { BashEnvironment, CommandRegistry, MAX_TIMEOUT_MS, heredocDelimiter, shellQuote } from "@demicodes/shell";
4
4
  import { providerRuntime } from "@demicodes/provider";
@@ -2119,7 +2119,6 @@ const APPROX_CHARS_PER_TOKEN = 4;
2119
2119
  const TOOL_DESCRIPTION_FIELD = "Concise title for the concrete user-visible state or result to make visible or confirm. Do not describe waiting, pausing, tool mechanics, generic actions, object labels, steps, tool names, ids, internals, or reasons.";
2120
2120
  const execRepeatStates = /* @__PURE__ */ new WeakMap();
2121
2121
  function createStandardAgentTools(options) {
2122
- const { environment } = options;
2123
2122
  return [
2124
2123
  {
2125
2124
  name: "shell_exec",
@@ -2144,6 +2143,7 @@ function createStandardAgentTools(options) {
2144
2143
  },
2145
2144
  invoke: async (ctx, input) => {
2146
2145
  const parsed = parseShellExecInput(input);
2146
+ const environment = await resolveEnvironment(options.environment, ctx, { shellId: parsed.shellId });
2147
2147
  const repeatGuard = repeatedShellExecResult(environment, ctx.agentSessionId, parsed.script);
2148
2148
  if (repeatGuard) return repeatGuard;
2149
2149
  const result = await environment.exec({
@@ -2171,7 +2171,9 @@ function createStandardAgentTools(options) {
2171
2171
  }
2172
2172
  },
2173
2173
  invoke: async (ctx, input) => {
2174
- const result = await environment.status(parseShellStatusInput(input));
2174
+ const parsed = parseShellStatusInput(input);
2175
+ const environment = await resolveEnvironment(options.environment, ctx, { commandId: parsed.commandId });
2176
+ const result = await environment.status(parsed);
2175
2177
  ctx.emitProgress(result);
2176
2178
  return finishShellToolResult(environment, result, ctx);
2177
2179
  }
@@ -2193,8 +2195,10 @@ function createStandardAgentTools(options) {
2193
2195
  }
2194
2196
  },
2195
2197
  invoke: async (ctx, input) => {
2198
+ const parsed = parseShellWriteInput(input);
2199
+ const environment = await resolveEnvironment(options.environment, ctx, { commandId: parsed.commandId });
2196
2200
  const result = await environment.write({
2197
- ...parseShellWriteInput(input),
2201
+ ...parsed,
2198
2202
  signal: ctx.signal
2199
2203
  });
2200
2204
  ctx.emitProgress(result);
@@ -2217,7 +2221,9 @@ function createStandardAgentTools(options) {
2217
2221
  }
2218
2222
  },
2219
2223
  invoke: async (ctx, input) => {
2220
- const result = await environment.abort(parseShellAbortInput(input));
2224
+ const parsed = parseShellAbortInput(input);
2225
+ const environment = await resolveEnvironment(options.environment, ctx, { commandId: parsed.commandId });
2226
+ const result = await environment.abort(parsed);
2221
2227
  ctx.emitProgress(result);
2222
2228
  return {
2223
2229
  ...await finishShellToolResult(environment, result, ctx),
@@ -2248,6 +2254,9 @@ function createStandardAgentTools(options) {
2248
2254
  }
2249
2255
  ];
2250
2256
  }
2257
+ function resolveEnvironment(source, ctx, handle) {
2258
+ return typeof source === "function" ? source(ctx, handle) : source;
2259
+ }
2251
2260
  function shellPreviewBudgetTokens(contextWindow) {
2252
2261
  return contextWindow >= LARGE_CONTEXT_THRESHOLD_TOKENS ? LARGE_CONTEXT_PREVIEW_TOKENS : SMALL_CONTEXT_PREVIEW_TOKENS;
2253
2262
  }
@@ -2485,12 +2494,12 @@ function shellCommandHandleRequired(result, budgetTokens) {
2485
2494
  }
2486
2495
  //#endregion
2487
2496
  //#region src/server.ts
2488
- var RunCommandLineSessionNotFoundError = class extends Error {
2489
- agentSessionId;
2490
- constructor(agentSessionId) {
2491
- super(`runCommandLine: no session "${agentSessionId}" is open in this process`);
2492
- this.agentSessionId = agentSessionId;
2493
- this.name = "RunCommandLineSessionNotFoundError";
2497
+ var RunCommandLineShellNotFoundError = class extends Error {
2498
+ shellId;
2499
+ constructor(shellId) {
2500
+ super(`runCommandLine: shell "${shellId}" is not open in this process`);
2501
+ this.shellId = shellId;
2502
+ this.name = "RunCommandLineShellNotFoundError";
2494
2503
  }
2495
2504
  };
2496
2505
  var RunCommandLineCommandNotRegisteredError = class extends Error {
@@ -2518,7 +2527,7 @@ var AgentServer = class {
2518
2527
  providers;
2519
2528
  shellOptions;
2520
2529
  sessionOptions;
2521
- prepareSessionShell;
2530
+ prepareShell;
2522
2531
  bindings = /* @__PURE__ */ new Set();
2523
2532
  sessionOwnership = new SessionOwnershipRegistry();
2524
2533
  constructor(options) {
@@ -2526,7 +2535,7 @@ var AgentServer = class {
2526
2535
  this.providers = createProviderMap(options.providers);
2527
2536
  this.shellOptions = options.shell ?? {};
2528
2537
  this.sessionOptions = options.session ?? {};
2529
- this.prepareSessionShell = options.prepareSessionShell ?? null;
2538
+ this.prepareShell = options.prepareShell ?? null;
2530
2539
  }
2531
2540
  client() {
2532
2541
  const transports = createInProcessTransportPair();
@@ -2540,7 +2549,7 @@ var AgentServer = class {
2540
2549
  providers: this.providers,
2541
2550
  shell: this.shellOptions,
2542
2551
  session: this.sessionOptions,
2543
- prepareSessionShell: this.prepareSessionShell,
2552
+ prepareShell: this.prepareShell,
2544
2553
  sessions: this.sessionOwnership
2545
2554
  });
2546
2555
  this.bindings.add(binding);
@@ -2556,10 +2565,11 @@ var AgentServer = class {
2556
2565
  * Transport-agnostic: callers (e.g. LocalHost command bridge) supply their
2557
2566
  * own IPC; AgentServer only knows how to exec against the open session shell.
2558
2567
  */
2559
- async runCommandLine(agentSessionId, name, args, opts) {
2560
- const binding = this.sessionOwnership.get(agentSessionId);
2561
- if (!binding) throw new RunCommandLineSessionNotFoundError(agentSessionId);
2562
- return binding.runCommandLine(name, args, opts);
2568
+ async runCommandLine(shellId, name, args, opts) {
2569
+ const owners = [...this.bindings].filter((binding) => binding.hasShell(shellId));
2570
+ if (owners.length === 0) throw new RunCommandLineShellNotFoundError(shellId);
2571
+ if (owners.length > 1) throw new Error(`runCommandLine: shell id "${shellId}" is not unique`);
2572
+ return owners[0].runCommandLine(shellId, name, args, opts);
2563
2573
  }
2564
2574
  };
2565
2575
  /**
@@ -2588,11 +2598,13 @@ var AgentTransportBindingImpl = class {
2588
2598
  providers;
2589
2599
  shellOptions;
2590
2600
  sessionOptions;
2591
- prepareSessionShell;
2601
+ prepareShell;
2592
2602
  sessions;
2593
2603
  session = null;
2594
2604
  currentAgent = null;
2595
- currentEnvironment = null;
2605
+ environmentsByHost = /* @__PURE__ */ new Map();
2606
+ pendingEnvironmentsByHost = /* @__PURE__ */ new Map();
2607
+ currentCommandRegistry = null;
2596
2608
  currentCwd = null;
2597
2609
  currentProviderId = null;
2598
2610
  currentSessionId = null;
@@ -2606,7 +2618,7 @@ var AgentTransportBindingImpl = class {
2606
2618
  this.providers = options.providers;
2607
2619
  this.shellOptions = options.shell ?? {};
2608
2620
  this.sessionOptions = options.session ?? {};
2609
- this.prepareSessionShell = options.prepareSessionShell;
2621
+ this.prepareShell = options.prepareShell;
2610
2622
  this.sessions = options.sessions;
2611
2623
  this.unsubscribeTransport = this.transport.onFrame((frame) => {
2612
2624
  this.handleFrame(frame);
@@ -2800,11 +2812,10 @@ var AgentTransportBindingImpl = class {
2800
2812
  await this.sessions.claim(agentSessionId, this);
2801
2813
  this.currentSessionId = agentSessionId;
2802
2814
  const initialState = agent.initialState();
2803
- const provisionalHost = agent.host({
2815
+ const store = new HostAgentSessionStore((await agent.host({
2804
2816
  state: initialState,
2805
2817
  cwd: frame.cwd
2806
- });
2807
- const store = new HostAgentSessionStore(provisionalHost.store, agentSessionId);
2818
+ })).store, agentSessionId);
2808
2819
  const checkpoint = await store.loadCheckpoint();
2809
2820
  const restoring = checkpoint !== null && checkpoint.harnessName === agent.name;
2810
2821
  const state = restoring ? structuredClone(checkpoint.state) : initialState;
@@ -2812,24 +2823,13 @@ var AgentTransportBindingImpl = class {
2812
2823
  state,
2813
2824
  cwd: frame.cwd
2814
2825
  };
2815
- const host = restoring ? agent.host(harnessContext) : provisionalHost;
2816
2826
  const commands = agent.commands?.(harnessContext) ?? [];
2817
2827
  const commandRegistry = new CommandRegistry();
2818
2828
  for (const command of commands) commandRegistry.register(command);
2819
2829
  const commandNames = commandRegistry.list().map((command) => command.name);
2820
- const environment = new BashEnvironment({
2821
- ...this.prepareSessionShell ? await this.prepareSessionShell({
2822
- agentSessionId,
2823
- host,
2824
- commandNames,
2825
- shell: this.shellOptions
2826
- }) : this.shellOptions,
2827
- host,
2828
- commands: commandRegistry
2829
- });
2830
2830
  let sessionRef = null;
2831
2831
  const tools = createStandardAgentTools({
2832
- environment,
2832
+ environment: (ctx, handle) => this.resolveEnvironment(ctx, handle),
2833
2833
  scheduleYield: (ctx, durationMs) => {
2834
2834
  if (!sessionRef) throw new Error("AgentServer: session is not ready for yield scheduling");
2835
2835
  return sessionRef.scheduleYieldWakeup(durationMs, ctx.metadata);
@@ -2873,7 +2873,7 @@ var AgentTransportBindingImpl = class {
2873
2873
  sessionRef = session;
2874
2874
  this.session = session;
2875
2875
  this.currentAgent = agent;
2876
- this.currentEnvironment = environment;
2876
+ this.currentCommandRegistry = commandRegistry;
2877
2877
  this.currentCwd = frame.cwd;
2878
2878
  this.currentProviderId = frame.provider.providerId;
2879
2879
  this.currentCommandNames = new Set(commandNames);
@@ -2962,11 +2962,13 @@ var AgentTransportBindingImpl = class {
2962
2962
  async closeSession() {
2963
2963
  const session = this.session;
2964
2964
  const agent = this.currentAgent;
2965
- const environment = this.currentEnvironment;
2966
2965
  const cwd = this.currentCwd;
2967
2966
  try {
2968
2967
  if (session) await session.dispose();
2969
- if (environment) await environment.disposeAllShells();
2968
+ const pendingEnvironments = await Promise.allSettled(this.pendingEnvironmentsByHost.values());
2969
+ const environments = new Set(this.environmentsByHost.values());
2970
+ for (const result of pendingEnvironments) if (result.status === "fulfilled") environments.add(result.value);
2971
+ await Promise.all([...environments].map((environment) => environment.disposeAllShells()));
2970
2972
  if (session && agent && cwd) await agent.dispose?.({
2971
2973
  agentSessionId: session.id(),
2972
2974
  state: session.state(),
@@ -2978,7 +2980,9 @@ var AgentTransportBindingImpl = class {
2978
2980
  this.unsubscribeSession = null;
2979
2981
  this.session = null;
2980
2982
  this.currentAgent = null;
2981
- this.currentEnvironment = null;
2983
+ this.environmentsByHost.clear();
2984
+ this.pendingEnvironmentsByHost.clear();
2985
+ this.currentCommandRegistry = null;
2982
2986
  this.currentCwd = null;
2983
2987
  this.currentProviderId = null;
2984
2988
  this.currentCommandNames = /* @__PURE__ */ new Set();
@@ -2989,7 +2993,7 @@ var AgentTransportBindingImpl = class {
2989
2993
  }
2990
2994
  }
2991
2995
  async listConversations(cwd) {
2992
- const host = this.agent.host({
2996
+ const host = await this.agent.host({
2993
2997
  state: this.agent.initialState(),
2994
2998
  cwd
2995
2999
  });
@@ -3008,16 +3012,22 @@ var AgentTransportBindingImpl = class {
3008
3012
  });
3009
3013
  }
3010
3014
  async handleShellWrite(frame) {
3011
- if (!this.sessionFor("shell_write") || !this.currentEnvironment || !this.currentCwd) return;
3012
- const result = await this.currentEnvironment.write({
3015
+ const session = this.sessionFor("shell_write");
3016
+ if (!session || !this.currentCwd) return;
3017
+ const result = await (await this.resolveEnvironment({
3018
+ agentSessionId: session.id(),
3019
+ state: session.state(),
3020
+ cwd: this.currentCwd,
3021
+ metadata: frame.metadata ?? null
3022
+ }, { commandId: frame.commandId })).write({
3013
3023
  commandId: frame.commandId,
3014
3024
  stdin: frame.stdin
3015
3025
  });
3016
3026
  this.sendShellWriteResult(frame.commandId, result);
3017
3027
  }
3018
3028
  /** Backs `AgentServer.runCommandLine` — see its doc comment for the contract. */
3019
- async runCommandLine(name, args, opts) {
3020
- const environment = this.currentEnvironment;
3029
+ async runCommandLine(shellId, name, args, opts) {
3030
+ const environment = this.environmentForShell(shellId);
3021
3031
  const agentSessionId = this.currentSessionId;
3022
3032
  if (!environment || !agentSessionId) throw new Error("runCommandLine: session has no active shell environment");
3023
3033
  if (!this.currentCommandNames.has(name)) throw new RunCommandLineCommandNotRegisteredError(name);
@@ -3059,6 +3069,66 @@ var AgentTransportBindingImpl = class {
3059
3069
  await environment.disposeShell(result.shellId).catch(() => {});
3060
3070
  }
3061
3071
  }
3072
+ hasShell(shellId) {
3073
+ return this.environmentForShell(shellId) !== null;
3074
+ }
3075
+ async resolveEnvironment(ctx, handle) {
3076
+ const agent = this.currentAgent;
3077
+ const commandRegistry = this.currentCommandRegistry;
3078
+ if (!agent || !commandRegistry) throw new Error("Shell environment is not available before session open");
3079
+ const host = await agent.host({
3080
+ agentSessionId: ctx.agentSessionId,
3081
+ state: ctx.state,
3082
+ cwd: ctx.cwd,
3083
+ metadata: ctx.metadata
3084
+ });
3085
+ const environment = await this.environmentForHost(host, commandRegistry);
3086
+ const owner = handle.shellId ? this.environmentForShell(handle.shellId) : handle.commandId ? this.environmentForCommand(handle.commandId) : null;
3087
+ if (owner && owner !== environment) {
3088
+ const id = handle.shellId ?? handle.commandId;
3089
+ throw new Error(`Shell handle "${id}" belongs to a different Host`);
3090
+ }
3091
+ return environment;
3092
+ }
3093
+ async environmentForHost(host, commands) {
3094
+ const existing = this.environmentsByHost.get(host);
3095
+ if (existing) return existing;
3096
+ const pending = this.pendingEnvironmentsByHost.get(host);
3097
+ if (pending) return pending;
3098
+ const creation = this.createEnvironment(host, commands);
3099
+ this.pendingEnvironmentsByHost.set(host, creation);
3100
+ try {
3101
+ const environment = await creation;
3102
+ this.environmentsByHost.set(host, environment);
3103
+ return environment;
3104
+ } finally {
3105
+ this.pendingEnvironmentsByHost.delete(host);
3106
+ }
3107
+ }
3108
+ async createEnvironment(host, commands) {
3109
+ const agentSessionId = this.currentSessionId;
3110
+ if (!agentSessionId) throw new Error("Shell environment is not available before session open");
3111
+ return new BashEnvironment({
3112
+ ...this.prepareShell ? await this.prepareShell({
3113
+ agentSessionId,
3114
+ host,
3115
+ commandNames: commands.list().map((command) => command.name),
3116
+ shell: this.shellOptions
3117
+ }) : this.shellOptions,
3118
+ host,
3119
+ commands
3120
+ });
3121
+ }
3122
+ environmentForShell(shellId) {
3123
+ const matches = [...this.environmentsByHost.values()].filter((environment) => environment.getShell(shellId));
3124
+ if (matches.length > 1) throw new Error(`Shell id "${shellId}" is not unique in this session`);
3125
+ return matches[0] ?? null;
3126
+ }
3127
+ environmentForCommand(commandId) {
3128
+ const matches = [...this.environmentsByHost.values()].filter((environment) => environment.hasCommand(commandId));
3129
+ if (matches.length > 1) throw new Error(`Command id "${commandId}" is not unique in this session`);
3130
+ return matches[0] ?? null;
3131
+ }
3062
3132
  sessionFor(command) {
3063
3133
  if (!this.session) {
3064
3134
  this.send({
@@ -3224,4 +3294,4 @@ function createProviderMap(providers) {
3224
3294
  return map;
3225
3295
  }
3226
3296
  //#endregion
3227
- export { AgentClient, AgentServer, AgentSession, DEFAULT_TURN_RETRY_POLICY, RunCommandLineCommandNotRegisteredError, RunCommandLineSessionNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, TranscriptLog, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, finishShellToolResult, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
3297
+ export { AgentClient, AgentServer, AgentSession, DEFAULT_TURN_RETRY_POLICY, RunCommandLineCommandNotRegisteredError, RunCommandLineShellNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, TranscriptLog, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, finishShellToolResult, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
@@ -1,4 +1,4 @@
1
- import { n as AgentServerTransport, t as AgentClientTransport } from "./transport-qMjHM60a.mjs";
1
+ import { n as AgentServerTransport, t as AgentClientTransport } from "./transport-bAa-oKZZ.mjs";
2
2
  import { Readable, Writable } from "node:stream";
3
3
  //#region src/stdio-transport.d.ts
4
4
  declare function createStdioClientTransport(readable: Readable, writable: Writable): AgentClientTransport;
@@ -170,10 +170,16 @@ interface AgentHarnessContext<State> {
170
170
  state: State;
171
171
  cwd: string;
172
172
  }
173
+ /** Context used when a shell operation resolves its action-specific Host. */
174
+ interface AgentHostContext<State> extends AgentHarnessContext<State> {
175
+ agentSessionId: string;
176
+ metadata: AgentMetadata | null;
177
+ }
173
178
  interface AgentHarness<State = unknown> {
174
179
  name: string;
175
180
  initialState(): State;
176
- host(ctx: AgentHarnessContext<State>): Host;
181
+ /** Return the same Host object for calls that target the same execution environment. */
182
+ host(ctx: AgentHarnessContext<State> | AgentHostContext<State>): Host | Promise<Host>;
177
183
  commands?(ctx: AgentHarnessContext<State>): Command[];
178
184
  systemPrompt(ctx: AgentSystemPromptContext<State>): string;
179
185
  preamble?(ctx: AgentPromptContext<State>): string | null;
@@ -370,6 +376,7 @@ type ClientFrame = {
370
376
  type: 'shell_write';
371
377
  commandId: string;
372
378
  stdin: string;
379
+ metadata?: AgentMetadata;
373
380
  } | {
374
381
  type: 'list_conversations';
375
382
  cwd: string;
@@ -542,4 +549,4 @@ interface InProcessTransportPair {
542
549
  }
543
550
  declare function createInProcessTransportPair(): InProcessTransportPair;
544
551
  //#endregion
545
- export { AgentToolInvokeContext as A, DEFAULT_TURN_RETRY_POLICY as B, AgentSessionOptions as C, AgentSystemPromptContext as D, AgentSessionStore as E, CompactionWindow as F, isRetryableCode as H, DrainedTranscriptPatches as I, TranscriptLog as L, ExternalMutationReservation as M, SessionEvent as N, AgentTool as O, SessionEventListener as P, TranscriptOptions as R, AgentSessionCheckpoint as S, AgentSessionRestoreParams as T, resolveRetryPolicy as U, TurnRetryPolicy as V, retryDelayMs as W, AgentHarnessRuntime as _, createInProcessTransportPair as a, AgentPromptContext as b, ConversationSummary as c, TranscriptPatch as d, AbortResult as f, AgentHarnessContext as g, AgentHarness as h, InProcessTransportPair as i, AgentToolInvokeResult as j, AgentToolContext as k, ServerFrame as l, AgentDisposeContext as m, AgentServerTransport as n, ClientFrame as o, AbortTarget as p, AgentTransport as r, ClientSessionEvent as s, AgentClientTransport as t, ShellCommandStatusLike as u, AgentLifecycleEvent as v, AgentSessionParams as w, AgentReferenceResolveContext as x, AgentMetadata as y, estimateTranscriptBlockTokens as z };
552
+ export { AgentToolContext as A, estimateTranscriptBlockTokens as B, AgentSessionCheckpoint as C, AgentSessionStore as D, AgentSessionRestoreParams as E, SessionEventListener as F, retryDelayMs as G, TurnRetryPolicy as H, CompactionWindow as I, DrainedTranscriptPatches as L, AgentToolInvokeResult as M, ExternalMutationReservation as N, AgentSystemPromptContext as O, SessionEvent as P, TranscriptLog as R, AgentReferenceResolveContext as S, AgentSessionParams as T, isRetryableCode as U, DEFAULT_TURN_RETRY_POLICY as V, resolveRetryPolicy as W, AgentHarnessRuntime as _, createInProcessTransportPair as a, AgentMetadata as b, ConversationSummary as c, TranscriptPatch as d, AbortResult as f, AgentHarnessContext as g, AgentHarness as h, InProcessTransportPair as i, AgentToolInvokeContext as j, AgentTool as k, ServerFrame as l, AgentDisposeContext as m, AgentServerTransport as n, ClientFrame as o, AbortTarget as p, AgentTransport as r, ClientSessionEvent as s, AgentClientTransport as t, ShellCommandStatusLike as u, AgentHostContext as v, AgentSessionOptions as w, AgentPromptContext as x, AgentLifecycleEvent as y, TranscriptOptions as z };
@@ -177,12 +177,13 @@ var AgentClient = class {
177
177
  this.sendFrame({ type: "abort" });
178
178
  return wait;
179
179
  }
180
- shellWrite(commandId, stdin) {
180
+ shellWrite(commandId, stdin, options = {}) {
181
181
  const wait = this.waitForShellWrite(commandId);
182
182
  this.sendFrame({
183
183
  type: "shell_write",
184
184
  commandId,
185
- stdin
185
+ stdin,
186
+ metadata: options.metadata
186
187
  });
187
188
  return wait;
188
189
  }
@@ -1,4 +1,4 @@
1
- import { c as ConversationSummary, f as AbortResult, n as AgentServerTransport, s as ClientSessionEvent, t as AgentClientTransport, y as AgentMetadata } from "./transport-qMjHM60a.mjs";
1
+ import { b as AgentMetadata, c as ConversationSummary, f as AbortResult, n as AgentServerTransport, s as ClientSessionEvent, t as AgentClientTransport } from "./transport-bAa-oKZZ.mjs";
2
2
  import { ProviderSelection } from "@demicodes/provider";
3
3
  import { Block, UserContentBlock } from "@demicodes/core";
4
4
  //#region src/client.d.ts
@@ -43,7 +43,7 @@ declare class AgentClient {
43
43
  resume(options?: AgentActionOptions): Promise<void>;
44
44
  compact(options?: AgentActionOptions): Promise<void>;
45
45
  abort(): Promise<AbortResult>;
46
- shellWrite(commandId: string, stdin: string): Promise<void>;
46
+ shellWrite(commandId: string, stdin: string, options?: AgentActionOptions): Promise<void>;
47
47
  close(): Promise<void>;
48
48
  subscribe(listener: AgentClientListener): () => void;
49
49
  transcript(): {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@demicodes/agent",
3
3
  "description": "Session runtime and transport-neutral client and server protocol for Demi.",
4
- "version": "0.5.0",
4
+ "version": "0.6.1",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "exports": {
@@ -19,10 +19,10 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@demicodes/core": "^0.5.0",
23
- "@demicodes/provider": "^0.5.0",
24
- "@demicodes/shell": "^0.5.0",
25
- "@demicodes/utils": "^0.5.0"
22
+ "@demicodes/core": "^0.6.1",
23
+ "@demicodes/provider": "^0.6.1",
24
+ "@demicodes/shell": "^0.6.1",
25
+ "@demicodes/utils": "^0.6.1"
26
26
  },
27
27
  "license": "Apache-2.0",
28
28
  "main": "./dist/index.mjs",