@hachej/boring-agent 0.1.79 → 0.1.81

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,13 +1,18 @@
1
1
  import {
2
2
  AgentDefinitionValidationError,
3
+ AgentDeploymentValidationError,
4
+ OpaqueRefSchema,
5
+ Sha256DigestSchema,
3
6
  createAgentAssetDigest,
4
7
  createAgentDefinitionDigest,
8
+ createAgentDeploymentDigest,
5
9
  validateAgentDefinition,
10
+ validateAgentDeployment,
6
11
  validateTool
7
- } from "./chunk-Q42DWR3T.js";
12
+ } from "./chunk-S2HYQYJP.js";
8
13
  import {
9
14
  createAgentRuntimeBridge
10
- } from "./chunk-LCEBDU6G.js";
15
+ } from "./chunk-2AVRA73A.js";
11
16
  import {
12
17
  sessionStreamPath
13
18
  } from "./chunk-WSQ5QNIY.js";
@@ -22,7 +27,7 @@ import {
22
27
  StopReceiptSchema,
23
28
  extractToolUiMetadata,
24
29
  sanitizeToolUiMetadata2 as sanitizeToolUiMetadata
25
- } from "./chunk-P23XFC4W.js";
30
+ } from "./chunk-3RSYCAHO.js";
26
31
  import {
27
32
  createLogger,
28
33
  createPiCodingAgentHarness,
@@ -33,14 +38,15 @@ import {
33
38
  registerConfiguredModelProviders,
34
39
  setEnvDefault,
35
40
  withPiHarnessDefaults
36
- } from "./chunk-X4WT3TPU.js";
41
+ } from "./chunk-24VL7G32.js";
37
42
  import {
38
43
  safeCapture
39
44
  } from "./chunk-AQBXNPMD.js";
40
45
  import {
41
46
  AgentDefinitionErrorCode,
47
+ AgentDeploymentErrorCode,
42
48
  ErrorCode
43
- } from "./chunk-YVON2BHN.js";
49
+ } from "./chunk-YV6D7GCQ.js";
44
50
 
45
51
  // src/server/sandbox/direct/createDirectSandbox.ts
46
52
  import { spawn } from "child_process";
@@ -8446,11 +8452,12 @@ function createAgentRuntimeBridge2(config, options = {}) {
8446
8452
  }
8447
8453
  return createAgentRuntimeBridge({
8448
8454
  runtimeFactory: () => createRuntime(config, options),
8455
+ readiness: config.readiness,
8449
8456
  readinessRequirements: config.readinessRequirements
8450
8457
  });
8451
8458
  }
8452
8459
  async function createRuntime(config, options) {
8453
- const harnessFactory = config.harnessFactory ?? (await import("./createHarness-XHMR3QN3.js")).createPiCodingAgentHarness;
8460
+ const harnessFactory = config.harnessFactory ?? (await import("./createHarness-4VZW2XEV.js")).createPiCodingAgentHarness;
8454
8461
  const harnessInput = {
8455
8462
  tools: config.tools ?? [],
8456
8463
  cwd: config.workdir ?? DEFAULT_WORKDIR,
@@ -8702,15 +8709,155 @@ async function compileAgentDirectory(directory) {
8702
8709
  return Object.freeze({ definition, definitionDigest, assets });
8703
8710
  }
8704
8711
 
8712
+ // src/server/agentDefinition/resolveAgentDeployment.ts
8713
+ import { z } from "zod";
8714
+ var AuthorizedAgentDeploymentBindingSchema = z.object({
8715
+ workspaceId: OpaqueRefSchema,
8716
+ defaultDeploymentId: OpaqueRefSchema,
8717
+ workspaceCompositionDigest: Sha256DigestSchema
8718
+ });
8719
+ var RESOLVED_AGENT_DIGEST_DOMAIN = "boring-agent/resolved-agent:v1";
8720
+ var ResolvedAgentDigestInputSchema = z.object({
8721
+ workspaceId: OpaqueRefSchema,
8722
+ defaultDeploymentId: OpaqueRefSchema,
8723
+ workspaceCompositionDigest: Sha256DigestSchema,
8724
+ definitionDigest: Sha256DigestSchema,
8725
+ deploymentDigest: Sha256DigestSchema
8726
+ }).strict();
8727
+ async function createResolvedAgentDigest(input) {
8728
+ const parsed = ResolvedAgentDigestInputSchema.safeParse(input);
8729
+ if (!parsed.success) {
8730
+ const field = parsed.error.issues[0]?.path[0];
8731
+ const digestField = typeof field === "string" ? field : "workspaceId";
8732
+ throw invalidDeployment(digestField, `${digestField} is invalid`);
8733
+ }
8734
+ return createAgentAssetDigest(JSON.stringify({ domain: RESOLVED_AGENT_DIGEST_DOMAIN, ...parsed.data }));
8735
+ }
8736
+ function invalidDefinition(field, message) {
8737
+ return new AgentDefinitionValidationError({
8738
+ code: AgentDefinitionErrorCode.enum.AGENT_DEFINITION_INVALID,
8739
+ field,
8740
+ message
8741
+ });
8742
+ }
8743
+ function invalidDeployment(field, message) {
8744
+ return new AgentDeploymentValidationError({
8745
+ code: AgentDeploymentErrorCode.enum.AGENT_DEPLOYMENT_INVALID,
8746
+ field,
8747
+ message
8748
+ });
8749
+ }
8750
+ function parseBinding(raw) {
8751
+ const parsed = AuthorizedAgentDeploymentBindingSchema.safeParse(raw);
8752
+ if (parsed.success) return parsed.data;
8753
+ const issue = parsed.error.issues[0];
8754
+ const field = issue.path[0];
8755
+ const bindingField = typeof field === "string" ? field : "workspaceId";
8756
+ throw invalidDeployment(bindingField, `${bindingField} ${issue.message}`);
8757
+ }
8758
+ function assertEqual(actual, expected, field) {
8759
+ if (actual !== expected) {
8760
+ throw invalidDeployment(field, `${field} does not match the compiled agent bundle`);
8761
+ }
8762
+ }
8763
+ async function resolveAgentDeployment(bundle, deployment, authorizedBinding) {
8764
+ const binding = parseBinding(authorizedBinding);
8765
+ const definitionDigest = await createAgentDefinitionDigest({
8766
+ definition: bundle.definition,
8767
+ assets: bundle.assets
8768
+ });
8769
+ if (definitionDigest !== bundle.definitionDigest) {
8770
+ throw invalidDefinition(
8771
+ "definitionDigest",
8772
+ "definitionDigest does not match the compiled agent bundle"
8773
+ );
8774
+ }
8775
+ const validatedDeployment = validateAgentDeployment(deployment);
8776
+ if (!validatedDeployment.valid) {
8777
+ throw new AgentDeploymentValidationError(validatedDeployment.issues[0]);
8778
+ }
8779
+ const deploymentSnapshot = validatedDeployment.value;
8780
+ const deploymentDigest = await createAgentDeploymentDigest(deploymentSnapshot);
8781
+ assertEqual(
8782
+ deploymentSnapshot.definition.definitionId,
8783
+ bundle.definition.definitionId,
8784
+ "definition.definitionId"
8785
+ );
8786
+ assertEqual(
8787
+ deploymentSnapshot.definition.version,
8788
+ bundle.definition.version,
8789
+ "definition.version"
8790
+ );
8791
+ assertEqual(deploymentSnapshot.definition.digest, definitionDigest, "definition.digest");
8792
+ if (deploymentSnapshot.agentId !== "default") {
8793
+ throw invalidDeployment("agentId", "agentId must be default in schema version 1");
8794
+ }
8795
+ if (binding.defaultDeploymentId !== deploymentSnapshot.deploymentId) {
8796
+ throw invalidDeployment(
8797
+ "defaultDeploymentId",
8798
+ "defaultDeploymentId does not match the resolved deployment"
8799
+ );
8800
+ }
8801
+ const instructions = bundle.assets.find(
8802
+ (asset) => asset.path === bundle.definition.instructionsRef
8803
+ );
8804
+ if (instructions === void 0) {
8805
+ throw invalidDefinition(
8806
+ "instructionsRef",
8807
+ "instructionsRef must name an included verified asset"
8808
+ );
8809
+ }
8810
+ const resolvedDigest = await createResolvedAgentDigest({
8811
+ workspaceId: binding.workspaceId,
8812
+ defaultDeploymentId: binding.defaultDeploymentId,
8813
+ workspaceCompositionDigest: binding.workspaceCompositionDigest,
8814
+ definitionDigest,
8815
+ deploymentDigest
8816
+ });
8817
+ const workspace = Object.freeze({
8818
+ workspaceId: binding.workspaceId,
8819
+ defaultDeploymentId: binding.defaultDeploymentId,
8820
+ compositionDigest: binding.workspaceCompositionDigest
8821
+ });
8822
+ const resolvedDeployment = Object.freeze({
8823
+ deploymentId: deploymentSnapshot.deploymentId,
8824
+ version: deploymentSnapshot.version,
8825
+ agentId: deploymentSnapshot.agentId,
8826
+ digest: deploymentDigest
8827
+ });
8828
+ const definition = Object.freeze({
8829
+ definitionId: bundle.definition.definitionId,
8830
+ version: bundle.definition.version,
8831
+ digest: definitionDigest,
8832
+ instructionsRef: bundle.definition.instructionsRef
8833
+ });
8834
+ const resolvedInstructions = Object.freeze({
8835
+ ref: bundle.definition.instructionsRef,
8836
+ content: instructions.content
8837
+ });
8838
+ return Object.freeze({
8839
+ workspace,
8840
+ deployment: resolvedDeployment,
8841
+ definition,
8842
+ instructions: resolvedInstructions,
8843
+ resolvedDigest
8844
+ });
8845
+ }
8846
+
8705
8847
  // src/server/mcp/managedAgentDelegate.ts
8706
- import { randomUUID } from "crypto";
8848
+ import { createHash as createHash6, randomUUID } from "crypto";
8707
8849
  var MANAGED_AGENT_MCP_ORIGIN_SURFACE = "mcp-managed-agent";
8708
- var MANAGED_AGENT_MCP_DELIVERY_RULE = "M1-pr1 DELIVERY v0: delegate_task returns final assistant text and artifact file references only; share-link delivery is gated on PR #424.";
8709
- var DEFAULT_MAX_BRIEF_CHARS = 12e3;
8710
- var DEFAULT_MAX_INLINE_ARTIFACT_CONTENT_CHARS = 8e3;
8850
+ var MANAGED_AGENT_MCP_DELIVERY_RULE = "M1 DELIVERY v0: delegate_task returns bounded final assistant text and at most one complete authorized inline Markdown artifact; no artifact paths, truncation, or share-link delivery.";
8851
+ var MAX_FINAL_ASSISTANT_TEXT_BYTES = 96 * 1024;
8852
+ var MAX_ARTIFACT_BYTES = 256 * 1024;
8853
+ var MAX_SERIALIZED_RESULT_BYTES = 384 * 1024;
8854
+ var MARKDOWN_MEDIA_TYPE = "text/markdown";
8855
+ var DEFAULT_MAX_BRIEF_BYTES = 32 * 1024;
8711
8856
  var DEFAULT_TERMINAL_RETENTION_MS = 15 * 6e4;
8712
8857
  var DEFAULT_MAX_DELEGATIONS = 100;
8713
8858
  var MAX_RETAINED_PROGRESS = 100;
8859
+ var utf8Encoder = new TextEncoder();
8860
+ var strictUtf8Decoder = new TextDecoder("utf-8", { fatal: true });
8714
8861
  var ManagedAgentMcpError = class extends Error {
8715
8862
  code;
8716
8863
  constructor(code, message) {
@@ -8724,8 +8871,8 @@ var ManagedAgentMcpDelegateController = class {
8724
8871
  this.options = options;
8725
8872
  this.createDelegationId = options.createDelegationId ?? randomUUID;
8726
8873
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
8727
- this.maxBriefChars = options.maxBriefChars ?? DEFAULT_MAX_BRIEF_CHARS;
8728
- this.maxInlineArtifactContentChars = options.maxInlineArtifactContentChars ?? DEFAULT_MAX_INLINE_ARTIFACT_CONTENT_CHARS;
8874
+ this.maxBriefBytes = options.maxBriefBytes ?? DEFAULT_MAX_BRIEF_BYTES;
8875
+ this.maxBriefChars = options.maxBriefChars;
8729
8876
  this.terminalRetentionMs = Math.max(0, options.terminalRetentionMs ?? DEFAULT_TERMINAL_RETENTION_MS);
8730
8877
  this.maxDelegations = Math.max(1, Math.floor(options.maxDelegations ?? DEFAULT_MAX_DELEGATIONS));
8731
8878
  this.redactionCanaries = options.redactionCanaries ?? [];
@@ -8734,8 +8881,8 @@ var ManagedAgentMcpDelegateController = class {
8734
8881
  delegations = /* @__PURE__ */ new Map();
8735
8882
  createDelegationId;
8736
8883
  now;
8884
+ maxBriefBytes;
8737
8885
  maxBriefChars;
8738
- maxInlineArtifactContentChars;
8739
8886
  terminalRetentionMs;
8740
8887
  maxDelegations;
8741
8888
  redactionCanaries;
@@ -8755,19 +8902,13 @@ var ManagedAgentMcpDelegateController = class {
8755
8902
  await input.onDelegationCreated?.(this.getStatus(delegationId, ctx));
8756
8903
  this.assertNotAborted(input.signal);
8757
8904
  const actor = await this.resolveActor(brief, ctx, request);
8758
- const receipt = await this.options.agent.start({
8759
- content: brief,
8760
- actor,
8761
- ctx,
8762
- originSurface: MANAGED_AGENT_MCP_ORIGIN_SURFACE
8763
- });
8764
- if (input.signal?.aborted) {
8765
- await this.stopDelegatedSession(receipt.sessionId, ctx);
8766
- throw new ManagedAgentMcpError(ErrorCode.enum.ABORTED, "delegation was cancelled");
8767
- }
8905
+ const bound = await this.resolveRunnerWorkspace(brief, ctx, request, actor);
8906
+ this.assertNotAborted(input.signal);
8907
+ let activeSessionId;
8768
8908
  let abortStopPromise;
8769
8909
  const abortListener = () => {
8770
- abortStopPromise ??= this.stopDelegatedSession(receipt.sessionId, ctx);
8910
+ if (!activeSessionId) return;
8911
+ abortStopPromise ??= this.stopDelegatedSession(bound.runner, activeSessionId, ctx);
8771
8912
  };
8772
8913
  input.signal?.addEventListener("abort", abortListener, { once: true });
8773
8914
  await this.pushProgress(record, "agent-started", "Agent session accepted for delegated task.", input.onProgress);
@@ -8775,11 +8916,21 @@ var ManagedAgentMcpDelegateController = class {
8775
8916
  let terminal = false;
8776
8917
  let terminalStatus;
8777
8918
  try {
8778
- for await (const event of this.options.agent.stream(receipt.sessionId, { startIndex: receipt.startIndex, ctx })) {
8919
+ for await (const event of bound.runner.run({
8920
+ brief,
8921
+ ctx,
8922
+ request,
8923
+ actor,
8924
+ signal: input.signal,
8925
+ onSessionStarted: (sessionId) => {
8926
+ activeSessionId = sessionId;
8927
+ }
8928
+ })) {
8779
8929
  if (input.signal?.aborted) {
8780
8930
  await abortStopPromise;
8781
8931
  throw new ManagedAgentMcpError(ErrorCode.enum.ABORTED, "delegation was cancelled");
8782
8932
  }
8933
+ activeSessionId = event.sessionId;
8783
8934
  events.push(event);
8784
8935
  await this.observeEvent(record, event, input.onProgress);
8785
8936
  if (isTerminalAgentEvent(event)) {
@@ -8801,20 +8952,21 @@ var ManagedAgentMcpDelegateController = class {
8801
8952
  if (terminalStatus !== "ok") {
8802
8953
  throw terminalError(terminalStatus, events);
8803
8954
  }
8804
- const finalAssistantText = extractFinalAssistantText(events);
8955
+ const finalAssistantText = this.validateFinalAssistantText(extractFinalAssistantText(events));
8805
8956
  const artifacts = await this.collectArtifacts({
8806
8957
  delegationId,
8807
- sessionId: receipt.sessionId,
8958
+ sessionId: activeSessionId ?? "",
8808
8959
  ctx,
8960
+ request,
8809
8961
  finalAssistantText,
8810
8962
  events
8811
- });
8963
+ }, bound.workspace);
8812
8964
  const result = {
8813
8965
  delegationId,
8814
8966
  status: "completed",
8815
8967
  finalAssistantText,
8816
- artifacts,
8817
- deliveryRule: MANAGED_AGENT_MCP_DELIVERY_RULE
8968
+ deliveryRule: MANAGED_AGENT_MCP_DELIVERY_RULE,
8969
+ ...artifacts[0] ? { artifact: artifacts[0] } : {}
8818
8970
  };
8819
8971
  this.assertPublicPayloadSafe(result);
8820
8972
  record.result = result;
@@ -8896,9 +9048,12 @@ var ManagedAgentMcpDelegateController = class {
8896
9048
  if (typeof value !== "string") throw new ManagedAgentMcpError(ErrorCode.enum.TOOL_INVALID_INPUT, "brief must be a string");
8897
9049
  const brief = value.trim();
8898
9050
  if (!brief) throw new ManagedAgentMcpError(ErrorCode.enum.TOOL_INVALID_INPUT, "brief is required");
8899
- if (brief.length > this.maxBriefChars) {
9051
+ if (this.maxBriefChars !== void 0 && brief.length > this.maxBriefChars) {
8900
9052
  throw new ManagedAgentMcpError(ErrorCode.enum.TOOL_INVALID_INPUT, `brief must be ${this.maxBriefChars} characters or fewer`);
8901
9053
  }
9054
+ if (utf8ByteLength(brief) > this.maxBriefBytes) {
9055
+ throw new ManagedAgentMcpError(ErrorCode.enum.TOOL_INVALID_INPUT, `brief must be ${this.maxBriefBytes} UTF-8 bytes or fewer`);
9056
+ }
8902
9057
  return brief;
8903
9058
  }
8904
9059
  async resolveSessionCtx(brief, request) {
@@ -8911,11 +9066,56 @@ var ManagedAgentMcpDelegateController = class {
8911
9066
  }
8912
9067
  return { workspaceId: ctx.workspaceId, userId: ctx.userId };
8913
9068
  }
9069
+ async resolveWorkspace(brief, ctx, request) {
9070
+ const resolveWorkspace = this.options.resolveWorkspace;
9071
+ if (!resolveWorkspace) {
9072
+ throw new ManagedAgentMcpError(ErrorCode.enum.CONFIG_INVALID, "MCP delegate requires a host-resolved Workspace");
9073
+ }
9074
+ const workspace = await resolveWorkspace({ brief, ctx, request });
9075
+ if (!workspace || typeof workspace.stat !== "function" || typeof workspace.readFile !== "function") {
9076
+ throw new ManagedAgentMcpError(ErrorCode.enum.CONFIG_INVALID, "MCP delegate requires a host-resolved Workspace");
9077
+ }
9078
+ return workspace;
9079
+ }
9080
+ async resolveRunnerWorkspace(brief, ctx, request, actor) {
9081
+ const resolved = this.options.resolveRunnerWorkspace ? await this.options.resolveRunnerWorkspace({ brief, ctx, request, actor }) : {
9082
+ runner: this.createAgentDelegateRunner(),
9083
+ workspace: await this.resolveWorkspace(brief, ctx, request)
9084
+ };
9085
+ if (!resolved || !resolved.runner || typeof resolved.runner.run !== "function") {
9086
+ throw new ManagedAgentMcpError(ErrorCode.enum.CONFIG_INVALID, "MCP delegate requires a host-resolved runner");
9087
+ }
9088
+ if (!resolved.workspace || typeof resolved.workspace.stat !== "function" || typeof resolved.workspace.readFile !== "function") {
9089
+ throw new ManagedAgentMcpError(ErrorCode.enum.CONFIG_INVALID, "MCP delegate requires a host-resolved Workspace");
9090
+ }
9091
+ return resolved;
9092
+ }
9093
+ createAgentDelegateRunner() {
9094
+ const agent = this.options.agent;
9095
+ if (!agent) {
9096
+ throw new ManagedAgentMcpError(ErrorCode.enum.CONFIG_INVALID, "MCP delegate requires a host-resolved runner");
9097
+ }
9098
+ return {
9099
+ async *run(input) {
9100
+ const receipt = await agent.start({
9101
+ content: input.brief,
9102
+ actor: input.actor,
9103
+ ctx: input.ctx,
9104
+ originSurface: MANAGED_AGENT_MCP_ORIGIN_SURFACE
9105
+ });
9106
+ input.onSessionStarted?.(receipt.sessionId);
9107
+ yield* agent.stream(receipt.sessionId, { startIndex: receipt.startIndex, ctx: input.ctx });
9108
+ },
9109
+ async stop(sessionId, ctx) {
9110
+ await agent.stop(sessionId, ctx);
9111
+ }
9112
+ };
9113
+ }
8914
9114
  assertNotAborted(signal) {
8915
9115
  if (signal?.aborted) throw new ManagedAgentMcpError(ErrorCode.enum.ABORTED, "delegation was cancelled");
8916
9116
  }
8917
- async stopDelegatedSession(sessionId, ctx) {
8918
- await this.options.agent.stop(sessionId, ctx).catch(() => void 0);
9117
+ async stopDelegatedSession(runner, sessionId, ctx) {
9118
+ await Promise.resolve(runner.stop?.(sessionId, ctx)).catch(() => void 0);
8919
9119
  }
8920
9120
  async resolveActor(brief, ctx, request) {
8921
9121
  const actor = await this.options.resolveActor?.({ brief, ctx, request });
@@ -8943,13 +9143,40 @@ var ManagedAgentMcpDelegateController = class {
8943
9143
  } catch {
8944
9144
  }
8945
9145
  }
8946
- async collectArtifacts(input) {
9146
+ validateFinalAssistantText(value) {
9147
+ const byteSize = utf8ByteLength(value);
9148
+ if (byteSize > MAX_FINAL_ASSISTANT_TEXT_BYTES) {
9149
+ throw new ManagedAgentMcpError(
9150
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_TOO_LARGE,
9151
+ `final assistant text must be ${MAX_FINAL_ASSISTANT_TEXT_BYTES} bytes or fewer`
9152
+ );
9153
+ }
9154
+ if (looksLikeSinglePath(value)) {
9155
+ throw new ManagedAgentMcpError(
9156
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID,
9157
+ "final assistant text must not be only an artifact path"
9158
+ );
9159
+ }
9160
+ return value;
9161
+ }
9162
+ async collectArtifacts(input, workspace) {
8947
9163
  const supplied = await this.options.collectArtifacts?.(input);
8948
- const artifacts = (supplied ?? extractArtifactRefs(input.events)).map(
8949
- (artifact) => normalizeArtifactRef(artifact, this.maxInlineArtifactContentChars)
8950
- );
8951
- this.assertPublicPayloadSafe(artifacts);
8952
- return artifacts;
9164
+ const rawCandidates = supplied ?? extractArtifactRefs(input.events);
9165
+ if (!Array.isArray(rawCandidates)) {
9166
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact references must be an array");
9167
+ }
9168
+ const candidates = rawCandidates.map((artifact2) => normalizeArtifactCandidate(artifact2));
9169
+ this.assertPublicPayloadSafe(candidates);
9170
+ if (candidates.length === 0) return [];
9171
+ if (candidates.length > 1) {
9172
+ throw new ManagedAgentMcpError(
9173
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID,
9174
+ "MCP delegate delivery supports at most one artifact"
9175
+ );
9176
+ }
9177
+ const artifact = await resolveMarkdownArtifact(candidates[0], workspace);
9178
+ this.assertPublicPayloadSafe(artifact);
9179
+ return [artifact];
8953
9180
  }
8954
9181
  toSafeError(error) {
8955
9182
  if (error instanceof ManagedAgentMcpError) {
@@ -8962,6 +9189,12 @@ var ManagedAgentMcpDelegateController = class {
8962
9189
  }
8963
9190
  assertPublicPayloadSafe(payload) {
8964
9191
  const serialized = JSON.stringify(payload);
9192
+ if (utf8ByteLength(serialized) > MAX_SERIALIZED_RESULT_BYTES) {
9193
+ throw new ManagedAgentMcpError(
9194
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_TOO_LARGE,
9195
+ `MCP delegate result must be ${MAX_SERIALIZED_RESULT_BYTES} bytes or fewer`
9196
+ );
9197
+ }
8965
9198
  if (this.containsSecret(serialized)) {
8966
9199
  throw new ManagedAgentMcpError(ErrorCode.enum.INTERNAL_ERROR, "MCP delegate payload failed secret redaction guard");
8967
9200
  }
@@ -9052,39 +9285,178 @@ function extractArtifactRefs(events) {
9052
9285
  }
9053
9286
  return artifacts;
9054
9287
  }
9055
- function normalizeArtifactRef(artifact, maxInlineArtifactContentChars) {
9056
- const path4 = normalizeArtifactPath(artifact.path);
9057
- const normalized = {
9288
+ function normalizeArtifactCandidate(artifact) {
9289
+ if (!artifact || typeof artifact !== "object") {
9290
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact reference is invalid");
9291
+ }
9292
+ const record = artifact;
9293
+ if (record.content !== void 0) {
9294
+ throw new ManagedAgentMcpError(
9295
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID,
9296
+ "artifact content must be resolved through the authorized workspace"
9297
+ );
9298
+ }
9299
+ if (record.truncated !== void 0) {
9300
+ throw new ManagedAgentMcpError(
9301
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID,
9302
+ "artifact content must be complete and untruncated"
9303
+ );
9304
+ }
9305
+ const path4 = normalizeArtifactPath(record.path);
9306
+ const mediaType = optionalNonEmptyString(record.mediaType);
9307
+ if (mediaType !== void 0 && mediaType.toLowerCase().split(";", 1)[0]?.trim() !== MARKDOWN_MEDIA_TYPE) {
9308
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact must be Markdown");
9309
+ }
9310
+ if (mediaType === void 0 && !isMarkdownPath(path4)) {
9311
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact must be Markdown");
9312
+ }
9313
+ const candidate = {
9058
9314
  path: path4,
9059
- mediaType: optionalNonEmptyString(artifact.mediaType),
9060
- title: optionalNonEmptyString(artifact.title)
9315
+ mediaType: MARKDOWN_MEDIA_TYPE
9061
9316
  };
9062
- if (artifact.content !== void 0) {
9063
- if (typeof artifact.content !== "string") {
9064
- throw new ManagedAgentMcpError(ErrorCode.enum.INTERNAL_ERROR, "artifact content must be text");
9065
- }
9066
- if (artifact.content.length <= maxInlineArtifactContentChars) {
9067
- normalized.content = artifact.content;
9068
- } else {
9069
- normalized.truncated = true;
9070
- }
9071
- }
9072
- if (artifact.truncated === true) normalized.truncated = true;
9073
- return normalized;
9317
+ const title = optionalNonEmptyString(record.title);
9318
+ if (title !== void 0) candidate.title = title;
9319
+ return candidate;
9074
9320
  }
9075
9321
  function normalizeArtifactPath(path4) {
9076
- if (typeof path4 !== "string") throw new ManagedAgentMcpError(ErrorCode.enum.INTERNAL_ERROR, "artifact path must be a string");
9322
+ if (typeof path4 !== "string") throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact path must be a string");
9077
9323
  const trimmed = path4.trim();
9078
- if (!trimmed) throw new ManagedAgentMcpError(ErrorCode.enum.INTERNAL_ERROR, "artifact path is required");
9079
- if (trimmed.includes("\0")) throw new ManagedAgentMcpError(ErrorCode.enum.INTERNAL_ERROR, "artifact path is invalid");
9080
- if (trimmed.startsWith("/") || /^[A-Za-z]:[\\/]/.test(trimmed)) {
9081
- throw new ManagedAgentMcpError(ErrorCode.enum.INTERNAL_ERROR, "artifact path must be workspace-relative");
9324
+ if (!trimmed) throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact path is required");
9325
+ if (trimmed.includes("\0")) throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact path is invalid");
9326
+ const decoded = safeDecodeArtifactPath(trimmed);
9327
+ const traversalCandidate = decoded.replace(/\\/g, "/");
9328
+ if (trimmed.startsWith("/") || traversalCandidate.startsWith("/") || /^[A-Za-z]:[\\/]/.test(trimmed) || /^[A-Za-z]:[\\/]/.test(traversalCandidate) || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(trimmed)) {
9329
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact path must be workspace-relative");
9330
+ }
9331
+ const parts = traversalCandidate.split("/");
9332
+ if (traversalCandidate.startsWith("~") || traversalCandidate.startsWith("$") || /[\r\n]/.test(traversalCandidate) || parts.some((part) => part === ".." || part.startsWith(".."))) {
9333
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact path must stay within the workspace");
9334
+ }
9335
+ const normalized = parts.filter((part) => part && part !== ".").join("/");
9336
+ if (!normalized) throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact path is required");
9337
+ return normalized;
9338
+ }
9339
+ async function resolveMarkdownArtifact(candidate, workspace) {
9340
+ const { content, bytes, byteSize } = await readStableArtifact(candidate.path, workspace);
9341
+ validateMarkdownContent(content);
9342
+ const artifact = {
9343
+ content,
9344
+ sha256: sha256(bytes),
9345
+ byteSize,
9346
+ mediaType: MARKDOWN_MEDIA_TYPE,
9347
+ ...candidate.title ? { title: candidate.title } : {}
9348
+ };
9349
+ return artifact;
9350
+ }
9351
+ async function readStableArtifact(path4, workspace) {
9352
+ const before = await statArtifact(workspace, path4);
9353
+ assertArtifactStat(before);
9354
+ if (before.size > MAX_ARTIFACT_BYTES) {
9355
+ throw new ManagedAgentMcpError(
9356
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_TOO_LARGE,
9357
+ `artifact must be ${MAX_ARTIFACT_BYTES} bytes or fewer`
9358
+ );
9082
9359
  }
9083
- const parts = trimmed.split(/[\\/]+/);
9084
- if (parts.some((part) => part === "..")) {
9085
- throw new ManagedAgentMcpError(ErrorCode.enum.INTERNAL_ERROR, "artifact path must stay within the workspace");
9360
+ const read = await readArtifactContent(workspace, path4);
9361
+ const after = await statArtifact(workspace, path4);
9362
+ assertArtifactStat(after);
9363
+ if (!sameStat(before, after) || read.byteSize !== before.size) {
9364
+ throw new ManagedAgentMcpError(
9365
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_UNAVAILABLE,
9366
+ "artifact changed while it was being read"
9367
+ );
9086
9368
  }
9087
- return parts.filter((part) => part && part !== ".").join("/");
9369
+ if (read.byteSize > MAX_ARTIFACT_BYTES) {
9370
+ throw new ManagedAgentMcpError(
9371
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_TOO_LARGE,
9372
+ `artifact must be ${MAX_ARTIFACT_BYTES} bytes or fewer`
9373
+ );
9374
+ }
9375
+ return read;
9376
+ }
9377
+ async function statArtifact(workspace, path4) {
9378
+ try {
9379
+ return await workspace.stat(path4);
9380
+ } catch (error) {
9381
+ throw artifactReadError(error);
9382
+ }
9383
+ }
9384
+ function assertArtifactStat(stat13) {
9385
+ if (stat13.kind !== "file") {
9386
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact must be a file");
9387
+ }
9388
+ if (!Number.isFinite(stat13.size) || stat13.size < 0) {
9389
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact size is invalid");
9390
+ }
9391
+ }
9392
+ async function readArtifactContent(workspace, path4) {
9393
+ try {
9394
+ if (!workspace.readBinaryFile) {
9395
+ throw new ManagedAgentMcpError(
9396
+ ErrorCode.enum.MCP_AGENT_ARTIFACT_UNAVAILABLE,
9397
+ "artifact bytes are unavailable through the authorized workspace"
9398
+ );
9399
+ }
9400
+ const bytes = await workspace.readBinaryFile(path4);
9401
+ const content = decodeUtf82(bytes);
9402
+ return { content, bytes, byteSize: bytes.byteLength };
9403
+ } catch (error) {
9404
+ if (error instanceof ManagedAgentMcpError) throw error;
9405
+ throw artifactReadError(error);
9406
+ }
9407
+ }
9408
+ function artifactReadError(error) {
9409
+ const code = error?.code;
9410
+ const reason = error?.reason;
9411
+ if (code === ErrorCode.enum.PATH_ESCAPE || code === ErrorCode.enum.PATH_ABSOLUTE || code === ErrorCode.enum.PATH_NULL_BYTE || code === ErrorCode.enum.PATH_SYMLINK_ESCAPE || reason === "path-escape" || reason === "absolute-path" || reason === "null-byte" || reason === "symlink-escape") {
9412
+ return new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact path is invalid");
9413
+ }
9414
+ return new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_UNAVAILABLE, "artifact is unavailable");
9415
+ }
9416
+ function decodeUtf82(bytes) {
9417
+ try {
9418
+ return strictUtf8Decoder.decode(bytes);
9419
+ } catch {
9420
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact must be well-formed UTF-8");
9421
+ }
9422
+ }
9423
+ function validateMarkdownContent(content) {
9424
+ if (!content.trim()) {
9425
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact Markdown must not be empty");
9426
+ }
9427
+ for (let index = 0; index < content.length; index += 1) {
9428
+ const code = content.charCodeAt(index);
9429
+ if (code === 0 || code < 32 && code !== 9 && code !== 10 && code !== 13) {
9430
+ throw new ManagedAgentMcpError(ErrorCode.enum.MCP_AGENT_ARTIFACT_INVALID, "artifact must be text Markdown");
9431
+ }
9432
+ }
9433
+ }
9434
+ function sameStat(left, right) {
9435
+ return left.kind === right.kind && left.size === right.size && left.mtimeMs === right.mtimeMs;
9436
+ }
9437
+ function sha256(bytes) {
9438
+ return `sha256:${createHash6("sha256").update(bytes).digest("hex")}`;
9439
+ }
9440
+ function safeDecodeArtifactPath(path4) {
9441
+ try {
9442
+ return decodeURIComponent(path4);
9443
+ } catch {
9444
+ return path4;
9445
+ }
9446
+ }
9447
+ function utf8ByteLength(value) {
9448
+ return utf8Encoder.encode(value).byteLength;
9449
+ }
9450
+ function isMarkdownPath(path4) {
9451
+ return /\.(md|markdown)$/i.test(path4);
9452
+ }
9453
+ function looksLikeSinglePath(value) {
9454
+ const trimmed = value.trim();
9455
+ if (!trimmed || /\s/.test(trimmed)) return false;
9456
+ if (!isMarkdownPath(trimmed)) return false;
9457
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed)) return true;
9458
+ if (trimmed.startsWith("/") || /^[A-Za-z]:[\\/]/.test(trimmed)) return true;
9459
+ return trimmed.includes("/") || trimmed.includes("\\");
9088
9460
  }
9089
9461
  function optionalNonEmptyString(value) {
9090
9462
  if (value === void 0) return void 0;
@@ -9099,18 +9471,21 @@ function sameSessionCtx(a, b) {
9099
9471
  // src/server/mcp/managedAgentMcpServer.ts
9100
9472
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9101
9473
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
9102
- import { z } from "zod";
9103
- var DEFAULT_MAX_BRIEF_CHARS2 = 12e3;
9474
+ import { z as z2 } from "zod";
9475
+ var DEFAULT_MAX_BRIEF_SCHEMA_CHARS = 32 * 1024;
9104
9476
  var delegateTaskStatusInputSchema = {
9105
- delegationId: z.string().min(1)
9477
+ delegationId: z2.string().min(1)
9106
9478
  };
9107
- function createManagedAgentMcpServer(options, controller = createManagedAgentMcpDelegateController(options)) {
9479
+ function createManagedAgentMcpServer(options) {
9480
+ return createManagedAgentMcpServerWithController(options, createManagedAgentMcpDelegateController(options));
9481
+ }
9482
+ function createManagedAgentMcpServerWithController(options, controller) {
9108
9483
  const server = new McpServer({
9109
9484
  name: options.name ?? "boring-managed-agent",
9110
9485
  version: options.version ?? "0.0.0"
9111
9486
  });
9112
9487
  const registerTool = server.registerTool.bind(server);
9113
- const delegateTaskInputSchema = createDelegateTaskInputSchema(options.maxBriefChars ?? DEFAULT_MAX_BRIEF_CHARS2);
9488
+ const delegateTaskInputSchema = createDelegateTaskInputSchema(options.maxBriefChars ?? DEFAULT_MAX_BRIEF_SCHEMA_CHARS);
9114
9489
  registerTool(
9115
9490
  "delegate_task",
9116
9491
  {
@@ -9186,7 +9561,7 @@ function createManagedAgentMcpServer(options, controller = createManagedAgentMcp
9186
9561
  function createManagedAgentMcpHttpHandler(options) {
9187
9562
  const controller = options.controller ?? createManagedAgentMcpDelegateController(options);
9188
9563
  return async (req, res, parsedBody) => {
9189
- const server = createManagedAgentMcpServer(options, controller);
9564
+ const server = createManagedAgentMcpServerWithController(options, controller);
9190
9565
  const transport = new StreamableHTTPServerTransport({
9191
9566
  sessionIdGenerator: void 0,
9192
9567
  enableJsonResponse: false
@@ -9207,7 +9582,7 @@ function requestContextFromExtra(extra) {
9207
9582
  }
9208
9583
  function createDelegateTaskInputSchema(maxBriefChars) {
9209
9584
  return {
9210
- brief: z.string().min(1).max(maxBriefChars)
9585
+ brief: z2.string().min(1).max(maxBriefChars)
9211
9586
  };
9212
9587
  }
9213
9588
  async function sendMcpProgress(extra, progress, message) {
@@ -11363,7 +11738,9 @@ function pathForWorkspaceEditor(workspaceRoot, filePath) {
11363
11738
  function skillsRoutes(app, opts, done) {
11364
11739
  const cached = /* @__PURE__ */ new Map();
11365
11740
  async function resolveSkillsForRequest(request, refresh = false) {
11366
- const workspaceRoot = opts.getWorkspaceRoot ? await opts.getWorkspaceRoot(request) : opts.workspaceRoot;
11741
+ const workspace = opts.getWorkspace ? await opts.getWorkspace(request) : opts.workspace;
11742
+ if (!workspace) throw new Error("skills route requires workspace or getWorkspace");
11743
+ const workspaceRoot = workspace.root;
11367
11744
  const additionalSkillPaths = opts.getAdditionalSkillPaths ? await opts.getAdditionalSkillPaths(request) : opts.additionalSkillPaths;
11368
11745
  const piPackages = opts.getPiPackages ? await opts.getPiPackages(request) : opts.piPackages;
11369
11746
  const noSkills = (opts.getNoSkills ? await opts.getNoSkills(request) : opts.noSkills) ?? withPiHarnessDefaults().noSkills;
@@ -11420,26 +11797,26 @@ function skillsRoutes(app, opts, done) {
11420
11797
 
11421
11798
  // src/server/http/routes/piChat.ts
11422
11799
  import { PassThrough } from "stream";
11423
- import { z as z2 } from "zod";
11800
+ import { z as z3 } from "zod";
11424
11801
  var DEFAULT_WORKSPACE_ID2 = "default";
11425
11802
  var DEFAULT_HEARTBEAT_INTERVAL_MS = 25e3;
11426
11803
  var DEFAULT_SESSION_LIST_LIMIT = 50;
11427
11804
  var MAX_SESSION_LIST_LIMIT = 100;
11428
11805
  var SAFE_SESSION_LIST_INCLUDE_ID = /^[a-zA-Z0-9_-]{1,128}$/;
11429
- var SessionParamsSchema = z2.object({
11430
- sessionId: z2.string().min(1).max(128)
11806
+ var SessionParamsSchema = z3.object({
11807
+ sessionId: z3.string().min(1).max(128)
11431
11808
  });
11432
- var CursorValueSchema = z2.preprocess((value) => {
11809
+ var CursorValueSchema = z3.preprocess((value) => {
11433
11810
  if (value === void 0) return 0;
11434
11811
  if (typeof value === "string" && value.length > 0) return Number(value);
11435
11812
  return value;
11436
- }, z2.number().int().nonnegative());
11437
- var EventsQuerySchema = z2.object({
11813
+ }, z3.number().int().nonnegative());
11814
+ var EventsQuerySchema = z3.object({
11438
11815
  cursor: CursorValueSchema
11439
11816
  });
11440
- var EmptyBodySchema = z2.preprocess((value) => value ?? {}, z2.object({}).strict());
11441
- var CreateSessionBodySchema = z2.preprocess((value) => value ?? {}, z2.object({
11442
- title: z2.string().min(1).max(200).optional()
11817
+ var EmptyBodySchema = z3.preprocess((value) => value ?? {}, z3.object({}).strict());
11818
+ var CreateSessionBodySchema = z3.preprocess((value) => value ?? {}, z3.object({
11819
+ title: z3.string().min(1).max(200).optional()
11443
11820
  }).strict());
11444
11821
  var PiChatRouteError = class extends Error {
11445
11822
  statusCode;
@@ -12238,8 +12615,8 @@ function requirePath(value, reply) {
12238
12615
  }
12239
12616
  function gitRoutes(app, opts, done) {
12240
12617
  async function resolveWorkspaceRoot(request) {
12241
- if (opts.getWorkspaceRoot) return await opts.getWorkspaceRoot(request);
12242
- return request.workspaceRoot;
12618
+ const workspace = opts.getWorkspace ? await opts.getWorkspace(request) : opts.workspace;
12619
+ return workspace === void 0 ? void 0 : getNodeWorkspaceHostRoot(workspace);
12243
12620
  }
12244
12621
  app.get("/api/v1/git/file-url", async (request, reply) => {
12245
12622
  const query = request.query;
@@ -12402,6 +12779,92 @@ function createWorkspaceAgentDispatcherError(code, message, statusCode) {
12402
12779
  return error;
12403
12780
  }
12404
12781
 
12782
+ // src/server/agentReadiness.ts
12783
+ var READINESS_PROBE_TOOL = {
12784
+ name: "agent_readiness_probe",
12785
+ description: "Internal readiness probe.",
12786
+ parameters: { type: "object", properties: {} },
12787
+ async execute() {
12788
+ return { content: [] };
12789
+ }
12790
+ };
12791
+ function collectToolReadinessRequirements(tools) {
12792
+ const requirements = /* @__PURE__ */ new Set();
12793
+ for (const tool of tools) {
12794
+ for (const requirement of tool.readinessRequirements ?? []) requirements.add(requirement);
12795
+ }
12796
+ return [...requirements];
12797
+ }
12798
+ function createAgentReadinessFromTracker(options) {
12799
+ const requirements = [...new Set(options.requirements)];
12800
+ return {
12801
+ requirements,
12802
+ async status() {
12803
+ return requirements.map((requirement) => readinessStatusForRequirement(requirement, options));
12804
+ }
12805
+ };
12806
+ }
12807
+ function readinessStatusForRequirement(requirement, options) {
12808
+ const snapshot = options.tracker.getReadiness();
12809
+ if (requirement === "workspace-fs") {
12810
+ return statusFromCapability(requirement, snapshot.capabilities.workspace, ErrorCode.enum.WORKSPACE_NOT_READY);
12811
+ }
12812
+ if (requirement === "sandbox-exec") {
12813
+ return snapshot.sandboxReady ? { key: requirement, ready: true, state: "ready" } : {
12814
+ key: requirement,
12815
+ ready: false,
12816
+ state: "preparing",
12817
+ errorCode: ErrorCode.enum.SANDBOX_NOT_READY,
12818
+ retryable: true
12819
+ };
12820
+ }
12821
+ if (isRuntimeReadinessRequirement(requirement)) {
12822
+ const status = statusFromCapability(
12823
+ requirement,
12824
+ snapshot.capabilities.runtimeDependencies,
12825
+ ErrorCode.enum.AGENT_RUNTIME_NOT_READY
12826
+ );
12827
+ if (status.ready || !options.checkReadiness) return status;
12828
+ return enrichRuntimeStatus(
12829
+ status,
12830
+ options.checkReadiness(requirement, READINESS_PROBE_TOOL)
12831
+ );
12832
+ }
12833
+ return { key: requirement, ready: false };
12834
+ }
12835
+ function enrichRuntimeStatus(status, readiness) {
12836
+ if (readiness === true || typeof readiness === "object" && readiness !== null && readiness.ready === true) {
12837
+ return status;
12838
+ }
12839
+ if (readiness === false) {
12840
+ return { ...status, retryable: status.retryable ?? true };
12841
+ }
12842
+ return {
12843
+ ...status,
12844
+ ...readiness.errorCode ? { errorCode: readiness.errorCode } : {},
12845
+ ...readiness.causeCode ? { causeCode: readiness.causeCode } : {},
12846
+ ...readiness.message ? { message: readiness.message } : {},
12847
+ ...readiness.workspaceId ? { workspaceId: readiness.workspaceId } : {},
12848
+ retryable: readiness.retryable ?? status.retryable ?? true
12849
+ };
12850
+ }
12851
+ function statusFromCapability(key, detail, fallbackErrorCode) {
12852
+ const ready = detail.state === "ready";
12853
+ const errorCode = detail.errorCode ?? (detail.state === "failed" && isRuntimeReadinessRequirement(key) ? ErrorCode.enum.RUNTIME_PROVISIONING_FAILED : fallbackErrorCode);
12854
+ return {
12855
+ key,
12856
+ ready,
12857
+ state: detail.state,
12858
+ ...!ready ? { errorCode } : {},
12859
+ ...detail.causeCode ? { causeCode: detail.causeCode } : {},
12860
+ ...detail.message ? { message: detail.message } : {},
12861
+ ...detail.retryable !== void 0 ? { retryable: detail.retryable } : {}
12862
+ };
12863
+ }
12864
+ function isRuntimeReadinessRequirement(requirement) {
12865
+ return requirement === "runtime-dependencies" || requirement.startsWith("runtime:");
12866
+ }
12867
+
12405
12868
  // src/server/createAgentApp.ts
12406
12869
  var DEFAULT_VERSION = "0.1.0-dev";
12407
12870
  var DEFAULT_SESSION_ID = "default";
@@ -12553,9 +13016,16 @@ async function createWorkspaceAgentAppProfile(opts, sessionId, resolvedMode, app
12553
13016
  sessionRoot: opts.sessionRoot,
12554
13017
  sessionDir: opts.sessionDir ?? input.sessionDir
12555
13018
  }));
13019
+ const readyTracker = createRuntimeReadyStatusTracker(modeAdapter, {
13020
+ harnessReady: true
13021
+ });
12556
13022
  const coreAgent = createAgentRuntimeBridge2({
12557
13023
  runtime: modeAdapter,
12558
13024
  tools,
13025
+ readiness: createAgentReadinessFromTracker({
13026
+ requirements: collectToolReadinessRequirements(tools),
13027
+ tracker: readyTracker
13028
+ }),
12559
13029
  harnessFactory,
12560
13030
  systemPromptAppend: opts.systemPromptAppend,
12561
13031
  systemPromptDynamic: opts.systemPromptDynamic,
@@ -12573,9 +13043,6 @@ async function createWorkspaceAgentAppProfile(opts, sessionId, resolvedMode, app
12573
13043
  opts.onWorkspaceAgentDispatcher?.(createStaticWorkspaceAgentDispatcherResolver(coreAgent.agent, sessionId));
12574
13044
  const harness = agentRuntime.harness;
12575
13045
  harnessRef = harness;
12576
- const readyTracker = createRuntimeReadyStatusTracker(modeAdapter, {
12577
- harnessReady: true
12578
- });
12579
13046
  const filesystemBindingsForRequest = opts.getFilesystemBindings ? (request) => {
12580
13047
  const user = request.user;
12581
13048
  return opts.getFilesystemBindings?.({
@@ -12588,6 +13055,8 @@ async function createWorkspaceAgentAppProfile(opts, sessionId, resolvedMode, app
12588
13055
  requestId: request.id
12589
13056
  });
12590
13057
  } : void 0;
13058
+ const gitStorageRoot = getOptionalRuntimeBundleStorageRoot(runtimeBundle);
13059
+ const gitWorkspace = gitStorageRoot === void 0 ? runtimeBundle.workspace : createNodeWorkspace(gitStorageRoot);
12591
13060
  return {
12592
13061
  runtimeMode: resolvedMode,
12593
13062
  capabilities: { tools: toolNames(tools) },
@@ -12611,12 +13080,12 @@ async function createWorkspaceAgentAppProfile(opts, sessionId, resolvedMode, app
12611
13080
  // File search shares the same bound implementation as the model tool.
12612
13081
  search: { fileSearch: runtimeBundle.fileSearch },
12613
13082
  // Git metadata resolves against host storage, not a sandbox-internal cwd.
12614
- git: { getWorkspaceRoot: () => getOptionalRuntimeBundleStorageRoot(runtimeBundle) }
13083
+ git: { workspace: gitWorkspace }
12615
13084
  },
12616
13085
  chat: { service: agentRuntime.service },
12617
13086
  systemPrompt: { harness },
12618
13087
  skills: {
12619
- workspaceRoot,
13088
+ workspace: createNodeWorkspace(workspaceRoot),
12620
13089
  additionalSkillPaths: runtimePi.additionalSkillPaths,
12621
13090
  piPackages: runtimePi.packages,
12622
13091
  noSkills: runtimePi.noSkills,
@@ -13194,7 +13663,7 @@ function createRuntimeProvisioningFailedError(workspaceId, cause) {
13194
13663
  }
13195
13664
  );
13196
13665
  }
13197
- function isRuntimeReadinessRequirement(requirement) {
13666
+ function isRuntimeReadinessRequirement2(requirement) {
13198
13667
  return requirement === "runtime-dependencies" || requirement.startsWith("runtime:");
13199
13668
  }
13200
13669
  function causeCodeFrom(error) {
@@ -13203,7 +13672,7 @@ function causeCodeFrom(error) {
13203
13672
  }
13204
13673
  function createRuntimeReadinessCheck(workspaceId, getRuntimeDependencies) {
13205
13674
  return (requirement) => {
13206
- if (!isRuntimeReadinessRequirement(requirement)) return true;
13675
+ if (!isRuntimeReadinessRequirement2(requirement)) return true;
13207
13676
  const runtimeDependencies = getRuntimeDependencies();
13208
13677
  if (runtimeDependencies.state === "ready" || runtimeDependencies.state === "not-started") return true;
13209
13678
  return {
@@ -13532,6 +14001,11 @@ var registerAgentRoutes = async (app, opts) => {
13532
14001
  const coreAgent = createAgentRuntimeBridge2({
13533
14002
  runtime: modeAdapter,
13534
14003
  tools,
14004
+ readiness: createAgentReadinessFromTracker({
14005
+ requirements: collectToolReadinessRequirements(tools),
14006
+ tracker: readyTracker,
14007
+ checkReadiness
14008
+ }),
13535
14009
  harnessFactory,
13536
14010
  systemPromptAppend: opts.systemPromptAppend,
13537
14011
  systemPromptDynamic,
@@ -13752,6 +14226,9 @@ var registerAgentRoutes = async (app, opts) => {
13752
14226
  }
13753
14227
  opts.onWorkspaceAgentDispatcher?.({
13754
14228
  async resolve(ctx, options) {
14229
+ return (await this.resolveWithWorkspace(ctx, options)).dispatcher;
14230
+ },
14231
+ async resolveWithWorkspace(ctx, options) {
13755
14232
  const boundCtx = normalizeWorkspaceAgentDispatcherContext(ctx);
13756
14233
  assertWorkspaceAgentDispatcherRequestContext(boundCtx, options?.request);
13757
14234
  bindingLifecycle.assertAdmission(boundCtx.workspaceId, options?.request);
@@ -13763,11 +14240,17 @@ var registerAgentRoutes = async (app, opts) => {
13763
14240
  401
13764
14241
  );
13765
14242
  }
13766
- return createLeasedWorkspaceAgentDispatcher(staticBinding, boundCtx, options?.request);
14243
+ return {
14244
+ dispatcher: createLeasedWorkspaceAgentDispatcher(staticBinding, boundCtx, options?.request),
14245
+ workspace: staticBinding.runtimeBundle.workspace
14246
+ };
13767
14247
  }
13768
14248
  const binding = await getOrCreateRuntimeBinding(boundCtx.workspaceId, options?.request, { trustedCtx: boundCtx });
13769
14249
  bindingLifecycle.assertAdmission(boundCtx.workspaceId, options?.request);
13770
- return createLeasedWorkspaceAgentDispatcher(binding, boundCtx, options?.request);
14250
+ return {
14251
+ dispatcher: createLeasedWorkspaceAgentDispatcher(binding, boundCtx, options?.request),
14252
+ workspace: binding.runtimeBundle.workspace
14253
+ };
13771
14254
  }
13772
14255
  });
13773
14256
  function getSkillsScopeForRequest(request) {
@@ -13867,7 +14350,11 @@ var registerAgentRoutes = async (app, opts) => {
13867
14350
  getFileSearch: async (request) => (await getBindingForRequest(request)).runtimeBundle.fileSearch
13868
14351
  });
13869
14352
  await app.register(gitRoutes, {
13870
- getWorkspaceRoot: async (request) => getOptionalRuntimeBundleStorageRoot((await getBindingForRequest(request)).runtimeBundle)
14353
+ getWorkspace: async (request) => {
14354
+ const runtimeBundle = (await getBindingForRequest(request)).runtimeBundle;
14355
+ const storageRoot = getOptionalRuntimeBundleStorageRoot(runtimeBundle);
14356
+ return storageRoot === void 0 ? runtimeBundle.workspace : createNodeWorkspace(storageRoot);
14357
+ }
13871
14358
  });
13872
14359
  await app.register(piChatRoutes, {
13873
14360
  getService: async (request) => {
@@ -13883,7 +14370,7 @@ var registerAgentRoutes = async (app, opts) => {
13883
14370
  filterModels: opts.filterModels
13884
14371
  });
13885
14372
  await app.register(skillsRoutes, {
13886
- workspaceRoot,
14373
+ workspace: staticBinding ? createNodeWorkspace(workspaceRoot) : void 0,
13887
14374
  additionalSkillPaths: [
13888
14375
  ...staticBinding?.runtimeProvisioning?.skillPaths ?? [],
13889
14376
  ...opts.pi?.additionalSkillPaths ?? []
@@ -13892,7 +14379,7 @@ var registerAgentRoutes = async (app, opts) => {
13892
14379
  // Undefined is fine: skillsRoutes resolves it through the canonical
13893
14380
  // harness policy (withPiHarnessDefaults), same as the factory above.
13894
14381
  noSkills: opts.pi?.noSkills,
13895
- getWorkspaceRoot: staticBinding ? void 0 : async (request) => (await getSkillsScopeForRequest(request)).root,
14382
+ getWorkspace: staticBinding ? void 0 : async (request) => createNodeWorkspace((await getSkillsScopeForRequest(request)).root),
13896
14383
  getAdditionalSkillPaths: staticBinding && !hasRuntimeProvisioningInput ? void 0 : async (request) => {
13897
14384
  const scope = await getSkillsScopeForRequest(request);
13898
14385
  if (!hasRuntimeProvisioningInput) return scope.pi.additionalSkillPaths;
@@ -14024,6 +14511,8 @@ export {
14024
14511
  createAgent,
14025
14512
  AgentDirectoryCompilerError,
14026
14513
  compileAgentDirectory,
14514
+ createResolvedAgentDigest,
14515
+ resolveAgentDeployment,
14027
14516
  MANAGED_AGENT_MCP_ORIGIN_SURFACE,
14028
14517
  MANAGED_AGENT_MCP_DELIVERY_RULE,
14029
14518
  ManagedAgentMcpError,