@hachej/boring-agent 0.1.93 → 0.1.95

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.
@@ -11,7 +11,7 @@ import {
11
11
  AgentEffectAdmissionError,
12
12
  createAgentRuntimeBridge,
13
13
  isObservedSynchronousServiceError
14
- } from "./chunk-CJ2BW2W2.js";
14
+ } from "./chunk-EOMY2HDT.js";
15
15
  import {
16
16
  AgentDefinitionValidationError,
17
17
  AgentDeploymentValidationError,
@@ -4077,12 +4077,22 @@ var HarnessPiChatService = class {
4077
4077
  }
4078
4078
  async readStateBeforeDispose(ctx, sessionId) {
4079
4079
  const sessionKey2 = this.sessionKey(ctx, sessionId);
4080
- const channel = this.channels.get(sessionKey2);
4080
+ let channel = this.channels.get(sessionKey2);
4081
4081
  if (!channel && !this.harnessMayHaveLiveSession(ctx, sessionId)) {
4082
4082
  const persisted = await this.readPersistedState(ctx, sessionId);
4083
- if (persisted) return persisted;
4083
+ channel = this.channels.get(sessionKey2);
4084
+ if (persisted && !channel && !this.harnessMayHaveLiveSession(ctx, sessionId)) return persisted;
4084
4085
  }
4085
4086
  const adapter = await this.getAdapter(ctx, sessionId, "");
4087
+ if (this.canRefreshFromPersistedState(sessionKey2, adapter)) {
4088
+ const persisted = await this.readPersistedState(ctx, sessionId);
4089
+ if (persisted && this.canRefreshFromPersistedState(sessionKey2, adapter)) {
4090
+ return this.enrichSyntheticPromptFailures(
4091
+ sessionKey2,
4092
+ this.messageMetadata.enrichSnapshot(sessionKey2, persisted)
4093
+ );
4094
+ }
4095
+ }
4086
4096
  const snapshot = this.messageMetadata.enrichSnapshot(sessionKey2, buildPiChatSnapshot(adapter, {
4087
4097
  seq: channel?.buffer.latestSeq ?? 0,
4088
4098
  sessionId,
@@ -4091,6 +4101,11 @@ var HarnessPiChatService = class {
4091
4101
  }));
4092
4102
  return this.enrichSyntheticPromptFailures(sessionKey2, snapshot);
4093
4103
  }
4104
+ canRefreshFromPersistedState(sessionKey2, adapter) {
4105
+ if (this.activePromptRuns.has(sessionKey2)) return false;
4106
+ const snapshot = adapter.readSnapshot();
4107
+ return !snapshot.isStreaming && !snapshot.isRetrying && snapshot.pendingMessageCount === 0 && snapshot.steeringMessages.length === 0 && snapshot.followUpMessages.length === 0;
4108
+ }
4094
4109
  harnessMayHaveLiveSession(ctx, sessionId) {
4095
4110
  return typeof this.harness.hasPiSession === "function" ? this.harness.hasPiSession(sessionId, toSessionCtx(ctx)) : true;
4096
4111
  }
@@ -4605,6 +4620,18 @@ var HarnessPiChatService = class {
4605
4620
  const seq = envelope?.chunk?.seq;
4606
4621
  return typeof seq === "number" && Number.isInteger(seq) && seq >= 0 ? seq : tailIndex + 1;
4607
4622
  }
4623
+ /**
4624
+ * Validate the persisted session identity and instantiate its lazily-created
4625
+ * Pi adapter under the same full cache key. This is intentionally a narrow
4626
+ * trusted-host seam rather than another browser route.
4627
+ */
4628
+ async ensurePiSessionBound(ctx, sessionId) {
4629
+ this.lifecycle.assertOpen();
4630
+ await this.assertCanAccessSession(ctx, sessionId);
4631
+ const adapter = await this.getAdapter(ctx, sessionId, "", { authorize: false });
4632
+ await this.ensureChannel(ctx, sessionId, adapter);
4633
+ this.lifecycle.assertOpen();
4634
+ }
4608
4635
  async assertCanAccessSession(ctx, sessionId) {
4609
4636
  try {
4610
4637
  await this.sessionStore.load(toSessionCtx(ctx), sessionId);
@@ -7311,7 +7338,16 @@ var EmbeddedAgentGateway = class {
7311
7338
  });
7312
7339
  this.runtime.activity.set(claim.workspaceScopeId, ref, "running");
7313
7340
  return { ...receipt, disposition: "prompt" };
7314
- }, true);
7341
+ }, {
7342
+ duplicateReceipt: true,
7343
+ guard: async () => await this.promptAdmission(
7344
+ scope,
7345
+ claim,
7346
+ ref,
7347
+ command.requestId,
7348
+ command.requireIdle === true
7349
+ )
7350
+ });
7315
7351
  }
7316
7352
  return await this.sessionEffect(ref, claim, "session.followup", command.requestId, command, async () => {
7317
7353
  const receipt = await service.followUp(context(claim, command.requestId), ref.sessionId, {
@@ -7321,7 +7357,7 @@ var EmbeddedAgentGateway = class {
7321
7357
  clientSeq: command.clientSeq
7322
7358
  });
7323
7359
  return { ...receipt, disposition: "followup" };
7324
- }, true);
7360
+ }, { duplicateReceipt: true });
7325
7361
  }
7326
7362
  async renameSession(input) {
7327
7363
  const claim = await this.verify(input.scope);
@@ -7395,16 +7431,18 @@ var EmbeddedAgentGateway = class {
7395
7431
  if (!summary) throw new AgentGatewayError(AgentGatewayErrorCode.AGENT_SESSION_NOT_FOUND, "session was not found");
7396
7432
  return summary;
7397
7433
  }
7398
- sessionEffect(ref, claim, operation, requestId, payload, action, duplicateReceipt = false, legacyAlias = false) {
7434
+ sessionEffect(ref, claim, operation, requestId, payload, action, options = {}) {
7399
7435
  return this.effect(
7400
7436
  claim,
7401
7437
  operation,
7402
7438
  sessionTarget(ref),
7403
7439
  requestId,
7404
7440
  payload,
7405
- () => this.withWriter(claim.workspaceScopeId, ref, action),
7406
- duplicateReceipt,
7407
- legacyAlias
7441
+ action,
7442
+ {
7443
+ ...options,
7444
+ serialize: (effect) => this.withWriter(claim.workspaceScopeId, ref, effect)
7445
+ }
7408
7446
  );
7409
7447
  }
7410
7448
  /** Internal legacy-alias seam; never exposed through the public Gateway contract. */
@@ -7419,8 +7457,7 @@ var EmbeddedAgentGateway = class {
7419
7457
  input.requestId,
7420
7458
  input.payload,
7421
7459
  input.action,
7422
- false,
7423
- true
7460
+ { legacyAlias: true }
7424
7461
  );
7425
7462
  }
7426
7463
  if (input.target.kind !== "session") throw new TypeError(`${input.operation} requires a session target`);
@@ -7431,12 +7468,20 @@ var EmbeddedAgentGateway = class {
7431
7468
  input.requestId,
7432
7469
  input.payload,
7433
7470
  input.action,
7434
- false,
7435
- true
7471
+ {
7472
+ legacyAlias: true,
7473
+ guard: input.retryableGuard
7474
+ }
7436
7475
  );
7437
7476
  }
7438
- async effect(claim, operation, target, requestId, payload, action, duplicateReceipt = false, legacyAlias = false) {
7477
+ async effect(claim, operation, target, requestId, payload, action, options = {}) {
7439
7478
  this.assertOpen();
7479
+ const {
7480
+ duplicateReceipt = false,
7481
+ legacyAlias = false,
7482
+ serialize,
7483
+ guard
7484
+ } = options;
7440
7485
  const key = {
7441
7486
  workspaceScopeId: claim.workspaceScopeId,
7442
7487
  authSubjectId: claim.authSubjectId,
@@ -7452,8 +7497,9 @@ var EmbeddedAgentGateway = class {
7452
7497
  const id = requestKeyString(key);
7453
7498
  const existing = this.effects.get(id);
7454
7499
  if (existing) return this.replayReceipt(await existing, duplicateReceipt);
7455
- const running = this.runtime.trackEffect((async () => {
7500
+ const execute = async () => {
7456
7501
  const current = await this.runtime.ledger.read(key);
7502
+ let admissionReceipt;
7457
7503
  if (current?.state === "pending-admission") {
7458
7504
  const admission = await this.runtime.effectAdmission.admit({ key, digest: digest2, scope: claim, operation, target });
7459
7505
  if (admission.type === "retryable") throw gatewayError(admission.error);
@@ -7461,71 +7507,87 @@ var EmbeddedAgentGateway = class {
7461
7507
  await this.runtime.ledger.reject(key, { kind: "gateway", error: admission.error });
7462
7508
  throw gatewayError(admission.error);
7463
7509
  }
7464
- await this.runtime.ledger.acceptAdmission(key, admission.admissionReceipt);
7465
- }
7466
- await this.runtime.ledger.beginEffect(key);
7467
- let actionResult;
7468
- try {
7469
- actionResult = action();
7470
- } catch (error) {
7471
- if (legacyAlias) {
7472
- if (error instanceof AgentEffectAdmissionError) {
7510
+ admissionReceipt = admission.admissionReceipt;
7511
+ }
7512
+ const runEffect = async () => {
7513
+ const locked = await this.runtime.ledger.read(key);
7514
+ if (locked?.state === "pending-admission") {
7515
+ if (admissionReceipt === void 0) {
7516
+ throw new AgentGatewayError(
7517
+ AgentGatewayErrorCode.AGENT_REQUEST_OUTCOME_UNKNOWN,
7518
+ "request admission receipt was unavailable"
7519
+ );
7520
+ }
7521
+ const retryableGuardError = await guard?.();
7522
+ if (retryableGuardError) throw gatewayError(retryableGuardError);
7523
+ await this.runtime.ledger.acceptAdmission(key, admissionReceipt);
7524
+ }
7525
+ await this.runtime.ledger.beginEffect(key);
7526
+ let actionResult;
7527
+ try {
7528
+ actionResult = action();
7529
+ } catch (error) {
7530
+ if (legacyAlias) {
7531
+ if (error instanceof AgentEffectAdmissionError) {
7532
+ const details = projectJson(error.details);
7533
+ const failure2 = {
7534
+ kind: "legacy-admission",
7535
+ code: error.code,
7536
+ statusCode: 500,
7537
+ message: error.message,
7538
+ ...details === void 0 ? {} : { details }
7539
+ };
7540
+ await this.runtime.ledger.reject(key, failure2);
7541
+ throw this.failure(failure2, true);
7542
+ }
7543
+ const failure = legacyServiceFailure(error);
7544
+ await this.runtime.ledger.reject(key, failure);
7545
+ throw this.failure(failure, true);
7546
+ }
7547
+ const unknown = new AgentGatewayError(
7548
+ AgentGatewayErrorCode.AGENT_REQUEST_OUTCOME_UNKNOWN,
7549
+ "effect outcome could not be safely replayed"
7550
+ );
7551
+ await this.runtime.ledger.markOutcomeUnknown(key, unknown.toJSON()).catch(() => {
7552
+ });
7553
+ throw error;
7554
+ }
7555
+ try {
7556
+ const receipt = await actionResult;
7557
+ this.runtime.assertOpen();
7558
+ await this.runtime.ledger.complete(key, receipt);
7559
+ return receipt;
7560
+ } catch (error) {
7561
+ if (legacyAlias && error instanceof AgentEffectAdmissionError) {
7473
7562
  const details = projectJson(error.details);
7474
- const failure2 = {
7563
+ const failure = {
7475
7564
  kind: "legacy-admission",
7476
7565
  code: error.code,
7477
7566
  statusCode: 500,
7478
7567
  message: error.message,
7479
7568
  ...details === void 0 ? {} : { details }
7480
7569
  };
7481
- await this.runtime.ledger.reject(key, failure2);
7482
- throw this.failure(failure2, true);
7570
+ await this.runtime.ledger.reject(key, failure);
7571
+ throw this.failure(failure, true);
7483
7572
  }
7484
- const failure = legacyServiceFailure(error);
7485
- await this.runtime.ledger.reject(key, failure);
7486
- throw this.failure(failure, true);
7487
- }
7488
- const unknown = new AgentGatewayError(
7489
- AgentGatewayErrorCode.AGENT_REQUEST_OUTCOME_UNKNOWN,
7490
- "effect outcome could not be safely replayed"
7491
- );
7492
- await this.runtime.ledger.markOutcomeUnknown(key, unknown.toJSON()).catch(() => {
7493
- });
7494
- throw error;
7495
- }
7496
- try {
7497
- const receipt = await actionResult;
7498
- this.runtime.assertOpen();
7499
- await this.runtime.ledger.complete(key, receipt);
7500
- return receipt;
7501
- } catch (error) {
7502
- if (legacyAlias && error instanceof AgentEffectAdmissionError) {
7503
- const details = projectJson(error.details);
7504
- const failure = {
7505
- kind: "legacy-admission",
7506
- code: error.code,
7507
- statusCode: 500,
7508
- message: error.message,
7509
- ...details === void 0 ? {} : { details }
7510
- };
7511
- await this.runtime.ledger.reject(key, failure);
7512
- throw this.failure(failure, true);
7513
- }
7514
- if (legacyAlias && (isObservedSynchronousServiceError(error) || isKnownLegacyServiceFailure(error))) {
7515
- const failure = legacyServiceFailure(error);
7516
- await this.runtime.ledger.reject(key, failure);
7517
- throw this.failure(failure, true);
7573
+ if (legacyAlias && (isObservedSynchronousServiceError(error) || isKnownLegacyServiceFailure(error))) {
7574
+ const failure = legacyServiceFailure(error);
7575
+ await this.runtime.ledger.reject(key, failure);
7576
+ throw this.failure(failure, true);
7577
+ }
7578
+ const unknown = new AgentGatewayError(
7579
+ AgentGatewayErrorCode.AGENT_REQUEST_OUTCOME_UNKNOWN,
7580
+ "effect outcome could not be safely replayed"
7581
+ );
7582
+ await this.runtime.ledger.markOutcomeUnknown(key, unknown.toJSON()).catch(() => {
7583
+ });
7584
+ if (legacyAlias) throw unknown;
7585
+ throw error;
7518
7586
  }
7519
- const unknown = new AgentGatewayError(
7520
- AgentGatewayErrorCode.AGENT_REQUEST_OUTCOME_UNKNOWN,
7521
- "effect outcome could not be safely replayed"
7522
- );
7523
- await this.runtime.ledger.markOutcomeUnknown(key, unknown.toJSON()).catch(() => {
7524
- });
7525
- if (legacyAlias) throw unknown;
7526
- throw error;
7527
- }
7528
- })(), key);
7587
+ };
7588
+ return serialize ? serialize(runEffect) : runEffect();
7589
+ };
7590
+ const running = this.runtime.trackEffect(execute(), key);
7529
7591
  this.effects.set(id, running);
7530
7592
  try {
7531
7593
  return await running;
@@ -7558,6 +7620,20 @@ var EmbeddedAgentGateway = class {
7558
7620
  "effect admission was rejected"
7559
7621
  );
7560
7622
  }
7623
+ async promptAdmission(scope, claim, ref, requestId, requireIdle) {
7624
+ const binding = await this.bindingForSession(scope, claim, ref);
7625
+ const snapshot = await binding.composition.service.readState(
7626
+ context(claim, requestId, binding.scope.identity),
7627
+ ref.sessionId
7628
+ );
7629
+ const admissible = requireIdle ? snapshot.status === "idle" : snapshot.status === "idle" || snapshot.status === "error";
7630
+ if (admissible) return void 0;
7631
+ return new AgentGatewayError(
7632
+ AgentGatewayErrorCode.AGENT_COMMAND_INVALID_STATE,
7633
+ requireIdle ? "session is not idle" : "prompt is invalid in current state",
7634
+ { status: snapshot.status }
7635
+ ).toJSON();
7636
+ }
7561
7637
  async withWriter(workspaceScopeId, ref, action) {
7562
7638
  const key = sessionKey(workspaceScopeId, ref);
7563
7639
  const previous = this.writerTails.get(key) ?? Promise.resolve();
@@ -8167,6 +8243,7 @@ var PromptBodySchema = z4.object({
8167
8243
  clientNonce: NonEmptyString.max(128),
8168
8244
  content: NonEmptyString.max(1e6),
8169
8245
  displayContent: NonEmptyString.max(1e6).optional(),
8246
+ requireIdle: z4.literal(true).optional(),
8170
8247
  model: ChatModelSelectionSchema.optional(),
8171
8248
  thinkingLevel: z4.enum(["off", "low", "medium", "high"]).optional(),
8172
8249
  attachments: z4.array(ChatAttachmentPayloadSchema).max(20).optional()
@@ -8486,7 +8563,8 @@ function createLegacyPiChatCompatibilityService(input) {
8486
8563
  target: options.target,
8487
8564
  requestId: options.requestId,
8488
8565
  payload: jsonProjection(options.payload),
8489
- action: options.action
8566
+ action: options.action,
8567
+ ...options.retryableGuard ? { retryableGuard: options.retryableGuard } : {}
8490
8568
  });
8491
8569
  return {
8492
8570
  ...input.service.listSessions ? { listSessions: (ctx, options) => input.service.listSessions(ctx, options) } : {},
@@ -8517,6 +8595,17 @@ function createLegacyPiChatCompatibilityService(input) {
8517
8595
  target: sessionTarget2(input.agentTypeId, sessionId),
8518
8596
  requestId: requestIdForPayload(ctx, payload.clientNonce),
8519
8597
  payload,
8598
+ ...payload.requireIdle ? {
8599
+ retryableGuard: async () => {
8600
+ const snapshot = await input.service.readState(ctx, sessionId);
8601
+ if (snapshot.status === "idle") return void 0;
8602
+ return new AgentGatewayError(
8603
+ AgentGatewayErrorCode.AGENT_COMMAND_INVALID_STATE,
8604
+ "session is not idle",
8605
+ { status: snapshot.status }
8606
+ ).toJSON();
8607
+ }
8608
+ } : {},
8520
8609
  action: () => input.service.prompt(ctx, sessionId, payload)
8521
8610
  }),
8522
8611
  followUp: (ctx, sessionId, payload) => effect({
@@ -9732,7 +9821,7 @@ async function dispatchGatewayInput(binding, input) {
9732
9821
  scope: binding.scope,
9733
9822
  agentTypeId: binding.agentTypeId,
9734
9823
  requestId,
9735
- title: contentToText(input.content ?? input.message).slice(0, 80) || void 0
9824
+ title: input.title?.trim() || contentToText(input.content ?? input.message).slice(0, 80) || void 0
9736
9825
  });
9737
9826
  const connection = await binding.gateway.connectSession({
9738
9827
  scope: binding.scope,
@@ -10897,6 +10986,75 @@ async function mountOrderedAgentHostLegacyRoutes(input) {
10897
10986
  );
10898
10987
  }
10899
10988
 
10989
+ // src/server/trustedPiSessionBinding.ts
10990
+ async function bindTrustedPiSession(input) {
10991
+ const sessionContext = trustedPiSessionContext(input.ctx, input.request, input.requested);
10992
+ await input.withServices(async ({ binding }) => {
10993
+ if (!binding.ensurePiSessionBound) {
10994
+ throw createWorkspaceAgentDispatcherError(
10995
+ ErrorCode.enum.INTERNAL_ERROR,
10996
+ "Pi session binding is unavailable",
10997
+ 500
10998
+ );
10999
+ }
11000
+ return await binding.ensurePiSessionBound(sessionContext, input.sessionId);
11001
+ });
11002
+ return {
11003
+ visibleUserMessageTarget: {
11004
+ async isIdle() {
11005
+ return await input.withServices(async ({ binding }) => {
11006
+ const snapshot = await binding.readState(sessionContext, input.sessionId);
11007
+ return snapshot.status === "idle";
11008
+ });
11009
+ },
11010
+ async sendIfIdle(sendInput) {
11011
+ const requestId = sendInput.requestId.trim();
11012
+ if (!requestId) throw new TypeError("requestId must be a non-empty string");
11013
+ try {
11014
+ const receipt = await input.withServices(async ({ prompt }) => await prompt.prompt(
11015
+ sessionContext,
11016
+ input.sessionId,
11017
+ {
11018
+ message: sendInput.message,
11019
+ displayMessage: sendInput.displayMessage ?? sendInput.message,
11020
+ clientNonce: requestId,
11021
+ requireIdle: true
11022
+ }
11023
+ ));
11024
+ return {
11025
+ status: "accepted",
11026
+ cursor: receipt.cursor,
11027
+ ...receipt.duplicate ? { duplicate: true } : {}
11028
+ };
11029
+ } catch (error) {
11030
+ if (error instanceof AgentGatewayError && error.code === AgentGatewayErrorCode.AGENT_COMMAND_INVALID_STATE) {
11031
+ return { status: "busy" };
11032
+ }
11033
+ if (isGoneSessionError(error)) return { status: "gone" };
11034
+ throw error;
11035
+ }
11036
+ }
11037
+ }
11038
+ };
11039
+ }
11040
+ function isGoneSessionError(error) {
11041
+ const code = error?.code;
11042
+ return code === AgentGatewayErrorCode.AGENT_SESSION_NOT_FOUND || code === AgentGatewayErrorCode.AGENT_SCOPE_DENIED || code === AgentGatewayErrorCode.AGENT_SESSION_RUNTIME_SCOPE_MISMATCH || code === ErrorCode.enum.SESSION_NOT_FOUND || code === ErrorCode.enum.UNAUTHORIZED;
11043
+ }
11044
+ function trustedPiSessionContext(ctx, request, requested) {
11045
+ if (requested?.workspaceId && requested.workspaceId !== ctx.workspaceId) {
11046
+ throw createWorkspaceAgentDispatcherError(ErrorCode.enum.UNAUTHORIZED, "Pi session workspace context mismatch", 401);
11047
+ }
11048
+ if (requested?.userId && requested.userId !== ctx.userId) {
11049
+ throw createWorkspaceAgentDispatcherError(ErrorCode.enum.UNAUTHORIZED, "Pi session user context mismatch", 401);
11050
+ }
11051
+ return {
11052
+ workspaceId: requested?.workspaceId ?? ctx.workspaceId,
11053
+ authSubject: requested?.userId ?? ctx.userId,
11054
+ requestId: request?.id ?? `trusted:${ctx.workspaceId}:${ctx.userId}`
11055
+ };
11056
+ }
11057
+
10900
11058
  // src/server/agentHostLegacyRouteRuntime.ts
10901
11059
  var DEFAULT_WORKSPACE_ID6 = "default";
10902
11060
  var STANDARD_AGENT_TOOL_NAMES = ["bash", "read", "write", "edit", "find", "grep", "ls"];
@@ -11391,6 +11549,11 @@ async function mountAgentHostLegacyRouteRuntime(app, opts, mountInput, scopePoli
11391
11549
  tools,
11392
11550
  readyTracker,
11393
11551
  piChatService: composition.service,
11552
+ trustedPiChatService: agentHost.createPiChatService({
11553
+ service: composition.service,
11554
+ scope: authorizedScope,
11555
+ agentTypeId: defaultAgentTypeId
11556
+ }),
11394
11557
  authorizedScope,
11395
11558
  hostScope
11396
11559
  };
@@ -11526,6 +11689,7 @@ async function mountAgentHostLegacyRouteRuntime(app, opts, mountInput, scopePoli
11526
11689
  throw error;
11527
11690
  }
11528
11691
  return {
11692
+ binding,
11529
11693
  dispatcher: createBoundWorkspaceAgentDispatcher({
11530
11694
  gateway: agentHost.gateway,
11531
11695
  scope: binding.authorizedScope,
@@ -11538,6 +11702,25 @@ async function mountAgentHostLegacyRouteRuntime(app, opts, mountInput, scopePoli
11538
11702
  binding = await getOrCreateRuntimeBinding(boundCtx.workspaceId, void 0, { trustedCtx: boundCtx });
11539
11703
  }
11540
11704
  }
11705
+ async function ensureTrustedPiSessionBound(initialBinding, boundCtx, boundSessionId, request, requestedSessionCtx) {
11706
+ return await bindTrustedPiSession({
11707
+ ctx: boundCtx,
11708
+ request,
11709
+ sessionId: boundSessionId,
11710
+ requested: requestedSessionCtx,
11711
+ withServices: async (effect) => {
11712
+ const operation = await acquireDispatcherOperation(initialBinding, boundCtx, request);
11713
+ try {
11714
+ return await effect({
11715
+ binding: operation.binding.piChatService,
11716
+ prompt: operation.binding.trustedPiChatService
11717
+ });
11718
+ } finally {
11719
+ operation.release();
11720
+ }
11721
+ }
11722
+ });
11723
+ }
11541
11724
  function createLeasedWorkspaceAgentDispatcher(initialBinding, boundCtx, request) {
11542
11725
  return {
11543
11726
  async dispatch(input) {
@@ -11616,14 +11799,28 @@ async function mountAgentHostLegacyRouteRuntime(app, opts, mountInput, scopePoli
11616
11799
  }
11617
11800
  return {
11618
11801
  dispatcher: createLeasedWorkspaceAgentDispatcher(staticBinding, boundCtx, options?.request),
11619
- workspace: staticBinding.runtimeBundle.workspace
11802
+ workspace: staticBinding.runtimeBundle.workspace,
11803
+ bindPiSession: async (boundSessionId, requestedSessionCtx) => await ensureTrustedPiSessionBound(
11804
+ staticBinding,
11805
+ boundCtx,
11806
+ boundSessionId,
11807
+ options?.request,
11808
+ requestedSessionCtx
11809
+ )
11620
11810
  };
11621
11811
  }
11622
11812
  const binding = await getOrCreateRuntimeBinding(boundCtx.workspaceId, options?.request, { trustedCtx: boundCtx });
11623
11813
  bindingLifecycle.assertAdmission(boundCtx.workspaceId, options?.request);
11624
11814
  return {
11625
11815
  dispatcher: createLeasedWorkspaceAgentDispatcher(binding, boundCtx, options?.request),
11626
- workspace: binding.runtimeBundle.workspace
11816
+ workspace: binding.runtimeBundle.workspace,
11817
+ bindPiSession: async (boundSessionId, requestedSessionCtx) => await ensureTrustedPiSessionBound(
11818
+ binding,
11819
+ boundCtx,
11820
+ boundSessionId,
11821
+ options?.request,
11822
+ requestedSessionCtx
11823
+ )
11627
11824
  };
11628
11825
  }
11629
11826
  });
@@ -42,6 +42,7 @@ var AGENT_EFFECT_METHODS = {
42
42
  };
43
43
  function withAgentEffectAdmission(service, admit) {
44
44
  return {
45
+ ...service.ensurePiSessionBound ? { ensurePiSessionBound: (ctx, sessionId) => service.ensurePiSessionBound(ctx, sessionId) } : {},
45
46
  ...service.listSessions ? { listSessions: (ctx, options) => service.listSessions(ctx, options) } : {},
46
47
  async createSession(ctx, init) {
47
48
  await admit(ctx);
@@ -1,8 +1,8 @@
1
1
  import { A as AgentHarness, a as AgentReadiness, b as Agent } from '../harness-Cc48o6uS.js';
2
2
  export { c as AGENT_NOT_IMPLEMENTED_UNTIL_T1, d as AgentActor, e as AgentCoreHarness, f as AgentCoreHarnessFactory, g as AgentCorePromptInput, h as AgentCoreSessionAdapter, i as AgentCoreSessionSnapshot, j as AgentEvent, k as AgentMessageContent, l as AgentMessagePart, m as AgentNotImplementedError, n as AgentReadinessStatus, o as AgentResolveInputResponse, p as AgentSendInput, q as AgentStartReceipt, r as AgentStreamOptions } from '../harness-Cc48o6uS.js';
3
3
  import { S as SessionStore } from '../piChatEvent-BsMp3MSo.js';
4
- import { A as AgentCoreSessionService, a as AgentEffectAdmission } from '../piChatSessionService-BrPqKrpY.js';
5
- export { b as AGENT_EFFECT_METHODS, c as AgentEffectAdmissionError, P as PiChatEventStreamResult, d as PiChatEventStreamSubscription, e as PiChatEventSubscriber, f as PiChatReplayRangeError, g as PiChatSessionService, h as PiSessionCreateInit, i as PiSessionRequestContext, w as withAgentEffectAdmission } from '../piChatSessionService-BrPqKrpY.js';
4
+ import { A as AgentCoreSessionService, a as AgentEffectAdmission } from '../piChatSessionService-C0KbcGw9.js';
5
+ export { b as AGENT_EFFECT_METHODS, c as AgentEffectAdmissionError, P as PiChatEventStreamResult, d as PiChatEventStreamSubscription, e as PiChatEventSubscriber, f as PiChatReplayRangeError, g as PiChatSessionService, h as PiSessionCreateInit, i as PiSessionRequestContext, w as withAgentEffectAdmission } from '../piChatSessionService-C0KbcGw9.js';
6
6
  import '@mariozechner/pi-coding-agent';
7
7
  import 'zod';
8
8
 
@@ -3,7 +3,7 @@ import {
3
3
  AgentEffectAdmissionError,
4
4
  createAgent,
5
5
  withAgentEffectAdmission
6
- } from "../chunk-CJ2BW2W2.js";
6
+ } from "../chunk-EOMY2HDT.js";
7
7
  import {
8
8
  AGENT_NOT_IMPLEMENTED_UNTIL_T1,
9
9
  AgentNotImplementedError
@@ -101,6 +101,8 @@ interface AgentPromptCommand {
101
101
  readonly clientNonce: string;
102
102
  readonly content: string;
103
103
  readonly displayContent?: string;
104
+ /** Require an actually idle Pi session; busy is a retryable admission result. */
105
+ readonly requireIdle?: true;
104
106
  readonly model?: ChatModelSelection;
105
107
  readonly thinkingLevel?: ThinkingLevel;
106
108
  readonly attachments?: readonly ChatAttachmentPayload[];