@hachej/boring-agent 0.1.105 → 0.1.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/{chunk-K25PK5EC.js → chunk-3JFPS2O6.js} +1 -0
  2. package/dist/{chunk-JNEKOVMJ.js → chunk-6OANSUEI.js} +1 -1
  3. package/dist/{chunk-4IQ4HHBG.js → chunk-7XAOVFSN.js} +7 -0
  4. package/dist/{chunk-F2OJI2LX.js → chunk-GS4ADE34.js} +863 -358
  5. package/dist/{chunk-LFN45QDZ.js → chunk-KDXIXVF6.js} +5076 -3640
  6. package/dist/{chunk-6DRHERVK.js → chunk-MVKXLMH3.js} +44 -2
  7. package/dist/chunk-YTUZFKB3.js +78 -0
  8. package/dist/core/index.d.ts +5 -5
  9. package/dist/core/index.js +2 -2
  10. package/dist/{createHarness-GMBZZPTN.js → createHarness-AHWSC36D.js} +3 -3
  11. package/dist/{errors-DH7C01Pm.d.ts → errors-CEU4iujR.d.ts} +59 -4
  12. package/dist/{file-search-qcD5zItr.d.ts → file-search-BGUHnW9o.d.ts} +3 -3
  13. package/dist/front/index.d.ts +36 -7
  14. package/dist/front/index.js +1491 -727
  15. package/dist/front/styles.css +57 -0
  16. package/dist/{harness-Dcd08yKr.d.ts → harness-Ct0e4RWc.d.ts} +6 -3
  17. package/dist/{lease-DffrPrQn.d.ts → lease-nASWv2tU.d.ts} +1 -1
  18. package/dist/{piChatEvent-iNSXiFQq.d.ts → piChatEvent-BUWIzw5l.d.ts} +67 -36
  19. package/dist/{piChatSessionService-DQSQ_kWZ.d.ts → piChatSessionService-nvroRAVk.d.ts} +13 -11
  20. package/dist/server/agent-host/testing/gatewayConformance.d.ts +6 -5
  21. package/dist/server/agent-host/testing/gatewayConformance.js +57 -8
  22. package/dist/server/index.d.ts +167 -212
  23. package/dist/server/index.js +13 -9
  24. package/dist/server/worker/index.js +7 -7
  25. package/dist/shared/index.d.ts +34 -28
  26. package/dist/shared/index.js +8 -8
  27. package/dist/{types-BTsdaAQ2.d.ts → types-B7-PN5jV.d.ts} +47 -14
  28. package/docs/AGENT_GATEWAY_V0.md +40 -2
  29. package/docs/ERROR_CODES.md +1 -0
  30. package/docs/README.md +1 -0
  31. package/docs/SESSION_IDENTITY.md +91 -0
  32. package/docs/tools.md +4 -0
  33. package/package.json +11 -10
  34. package/dist/chunk-WSQ5QNIY.js +0 -18
@@ -71,17 +71,17 @@ import {
71
71
  validateConfig,
72
72
  validateConsumptionGuards,
73
73
  validateTool
74
- } from "../chunk-JNEKOVMJ.js";
74
+ } from "../chunk-6OANSUEI.js";
75
75
  import {
76
76
  SAFE_NATIVE_SESSION_ID,
77
+ compareSessionOrder,
77
78
  noopTelemetry,
78
79
  safeCapture
79
- } from "../chunk-4IQ4HHBG.js";
80
+ } from "../chunk-7XAOVFSN.js";
80
81
  import {
81
82
  AGENT_NOT_IMPLEMENTED_UNTIL_T1,
82
- AgentNotImplementedError,
83
- sessionStreamPath
84
- } from "../chunk-WSQ5QNIY.js";
83
+ AgentNotImplementedError
84
+ } from "../chunk-YTUZFKB3.js";
85
85
  import {
86
86
  AGENT_GATEWAY_ERROR_CODES,
87
87
  AgentGatewayError,
@@ -114,7 +114,7 @@ import {
114
114
  extractToolUiMetadata,
115
115
  isToolUiMetadata,
116
116
  sanitizeToolUiMetadata2 as sanitizeToolUiMetadata
117
- } from "../chunk-6DRHERVK.js";
117
+ } from "../chunk-MVKXLMH3.js";
118
118
  import {
119
119
  safeRandomUUID
120
120
  } from "../chunk-YGRQ4Q5P.js";
@@ -127,7 +127,7 @@ import {
127
127
  ERROR_CODES,
128
128
  ErrorCode,
129
129
  ErrorLogFieldsSchema
130
- } from "../chunk-K25PK5EC.js";
130
+ } from "../chunk-3JFPS2O6.js";
131
131
  export {
132
132
  AGENT_GATEWAY_ERROR_CODES,
133
133
  AGENT_KNOWLEDGE_FILESYSTEM_ID,
@@ -219,6 +219,7 @@ export {
219
219
  assertNoConsumptionCycle,
220
220
  assertValidTransition,
221
221
  assertWithinConsumptionDepth,
222
+ compareSessionOrder,
222
223
  createAgentAssetDigest,
223
224
  createAgentDefinitionDigest,
224
225
  createAgentDeploymentDigest,
@@ -240,7 +241,6 @@ export {
240
241
  safeRandomUUID,
241
242
  sandboxCredentialPayloadMetadataBytesV1,
242
243
  sanitizeToolUiMetadata,
243
- sessionStreamPath,
244
244
  validateAgentDefinition,
245
245
  validateAgentDeployment,
246
246
  validateAgentTask,
@@ -1,10 +1,11 @@
1
1
  import { FastifyRequest, FastifyPluginAsync } from 'fastify';
2
- import { C as ToolReadinessRequirement, v as TelemetrySink, s as AgentTool, D as AgentHarnessFactory } from './harness-Dcd08yKr.js';
3
- import { F as FileSearch, b as AgentSkillResource, c as WorkspaceAgentDirectRunInput, L as LeaseBoundWorkspaceAgent } from './file-search-qcD5zItr.js';
2
+ import { C as ToolReadinessRequirement, w as TelemetrySink, s as AgentTool, D as AgentHarnessFactory } from './harness-Ct0e4RWc.js';
3
+ import { F as FileSearch, b as AgentSkillResource, c as WorkspaceAgentDirectRunInput, L as LeaseBoundWorkspaceAgent } from './file-search-BGUHnW9o.js';
4
4
  import { b as Workspace, h as RuntimeFilesystemCapability, j as WorkspaceRuntimeContext, c as Sandbox } from './sandbox-BMhut3kh.js';
5
- import { b as AgentSessionRef, V as VerifiedAgentScopeClaim, i as AgentGatewayErrorDTO, x as JsonValue, a as AuthorizedAgentScope, A as AgentGateway, k as AgentScopeVerifier } from './errors-DH7C01Pm.js';
6
- import { C as ChatModelSelection } from './piChatEvent-iNSXiFQq.js';
5
+ import { b as AgentSessionRef, V as VerifiedAgentScopeClaim, k as AgentGatewayErrorDTO, B as JsonValue, a as AuthorizedAgentScope, A as AgentGateway, m as AgentScopeVerifier, F as ResolveAgentAccess } from './errors-CEU4iujR.js';
6
+ import { C as ChatModelSelection } from './piChatEvent-BUWIzw5l.js';
7
7
  import { RuntimeHostOperations, RuntimeFilesystemBinding as RuntimeFilesystemBinding$1 } from '@hachej/boring-bash/agent';
8
+ import { BwrapArgsOptions } from '@hachej/boring-sandbox/providers/bwrap';
8
9
  import { CreateNodeWorkspaceOptions, BoringAgentRuntimePaths } from '@hachej/boring-sandbox/providers/node-workspace';
9
10
  import { PackageSource, ExtensionFactory } from '@mariozechner/pi-coding-agent';
10
11
 
@@ -239,6 +240,12 @@ declare class ReadyStatusTracker {
239
240
  * may inject an equivalent implementation with their Workspace + Sandbox pair.
240
241
  */
241
242
  interface AgentRuntimeHostOperations extends RuntimeHostOperations {
243
+ /**
244
+ * Agent legally depends on the provider package, so the injected builder is
245
+ * typed with the canonical options here rather than the narrowed slice
246
+ * boring-bash declares.
247
+ */
248
+ buildBwrapArgs(workspaceRoot: string, options?: BwrapArgsOptions): string[];
242
249
  createNodeWorkspace(root: string, options?: CreateNodeWorkspaceOptions): Workspace;
243
250
  getNodeWorkspaceHostRoot(workspace: Workspace): string | undefined;
244
251
  getBoringAgentRuntimePaths(workspaceRoot: string): BoringAgentRuntimePaths;
@@ -408,7 +415,8 @@ interface RuntimeModeAdapter {
408
415
  readonly readiness?: RuntimeModeReadinessHooks;
409
416
  readonly cachedBindingHealthCheck?: RuntimeCachedBindingHealthCheck;
410
417
  create(ctx: ModeContext): Promise<RuntimeBundle>;
411
- getRuntimeLayoutRoot?(ctx: ModeContext): string;
418
+ /** Resolves the provider-owned workspace root in runtime coordinates. */
419
+ getRuntimeLayoutRoot(ctx: ModeContext): string;
412
420
  evictCachedRuntime?(ctx: {
413
421
  workspaceId: string;
414
422
  }): void | Promise<void>;
@@ -529,15 +537,26 @@ interface RuntimeFilesystemBindingOperations {
529
537
  path: string;
530
538
  }): never;
531
539
  }
540
+ interface RuntimeFilesystemCatalogPresentation {
541
+ readonly visible?: boolean;
542
+ readonly label?: string;
543
+ readonly rootDir?: '.' | `/${string}`;
544
+ }
532
545
  interface RuntimeFilesystemBinding {
533
546
  readonly filesystem: string;
534
547
  readonly access: 'readonly' | 'readwrite';
535
548
  readonly operations: RuntimeFilesystemBindingOperations;
549
+ /** User-facing catalog projection; internal routing continues to use filesystem. */
550
+ readonly catalog?: RuntimeFilesystemCatalogPresentation;
551
+ /** When present, only these addressed Agents receive this user-visible binding. */
552
+ readonly agentTypeIds?: readonly string[];
536
553
  }
537
554
  interface RuntimeBundle {
538
555
  runtimeContext?: WorkspaceRuntimeContext;
539
556
  /**
540
- * Server-private host/storage root for host-side filesystem work. Do not use
557
+ * Server-private host/storage root for host-side filesystem work. Required
558
+ * when runtime-coordinate workspace resources have an explicit host mirror;
559
+ * absence means composition must not infer or project a host path. Do not use
541
560
  * this as the agent-visible cwd; Workspace.root remains the public runtime
542
561
  * namespace shown to tools/model.
543
562
  */
@@ -613,6 +632,11 @@ interface PiHarnessOptions {
613
632
  /** Reject an explicit unavailable/unknown model instead of silently falling back. */
614
633
  strictModelResolution?: boolean;
615
634
  }
635
+ /** Pi harness options with the discovery flags resolved to definite booleans. */
636
+ type ResolvedPiHarnessOptions = PiHarnessOptions & {
637
+ noContextFiles: boolean;
638
+ noSkills: boolean;
639
+ };
616
640
  interface HotReloadablePiResources {
617
641
  additionalSkillPaths?: string[];
618
642
  packages?: PiPackageSource[];
@@ -648,7 +672,7 @@ interface AgentSkillResourceSnapshot {
648
672
  locateSkill(filePath: string): AgentSkillResource | undefined;
649
673
  }
650
674
 
651
- type AgentGatewayEffect = 'session.create' | 'session.rename' | 'session.delete' | 'session.prompt' | 'session.followup' | 'session.interrupt' | 'session.stop' | 'session.queue.clear' | 'agent.reload' | 'session.command.execute';
675
+ type AgentGatewayEffect = 'session.create' | 'session.rename' | 'session.archive' | 'session.delete' | 'session.prompt' | 'session.followup' | 'session.interrupt' | 'session.stop' | 'session.queue.clear' | 'agent.reload' | 'session.command.execute';
652
676
  type AgentRequestTarget = {
653
677
  readonly kind: 'agent';
654
678
  readonly agentTypeId: string;
@@ -769,7 +793,8 @@ interface AgentInstructionSource {
769
793
  readonly absolutePath: string;
770
794
  readonly role: 'persona';
771
795
  }
772
- interface ConfiguredAgentHostAgentSpec {
796
+ declare const DEFAULT_AGENT_TYPE_ID = "default";
797
+ interface AgentHostAgentSpec {
773
798
  readonly agentTypeId: string;
774
799
  readonly definition: {
775
800
  readonly instructions: string;
@@ -806,12 +831,12 @@ interface ConfiguredAgentHostAgentSpec {
806
831
  /** RESERVED / NOT ENFORCED. Per-turn token-limit enforcement is future work. */
807
832
  readonly maxTokensPerTurn?: number;
808
833
  };
834
+ /** Trusted host-owned provisioning grants; absent means no inherited resources. */
835
+ readonly provisioning?: {
836
+ readonly inheritSkillPaths?: boolean;
837
+ };
809
838
  }
810
- interface LegacyDefaultAgentHostSpec {
811
- readonly agentTypeId: 'default';
812
- readonly legacyDefault: true;
813
- }
814
- type AgentHostAgentSpec = ConfiguredAgentHostAgentSpec | LegacyDefaultAgentHostSpec;
839
+ type ConfiguredAgentHostAgentSpec = AgentHostAgentSpec;
815
840
  /**
816
841
  * Server-only compiler output. App-specific validated handles may be attached,
817
842
  * but the Host revalidates the complete agent ID set before freezing it.
@@ -856,6 +881,12 @@ interface ResolvedAgentRuntimeScope {
856
881
  readonly sessionNamespace: string;
857
882
  readonly pi?: PiHarnessOptions;
858
883
  readonly extraTools?: readonly AgentTool[];
884
+ /** Joined trusted-plugin cleanup invoked after backend session deletion succeeds. */
885
+ readonly onSessionDelete?: (input: {
886
+ readonly workspaceScopeId: string;
887
+ readonly agentTypeId: string;
888
+ readonly sessionId: string;
889
+ }) => Promise<void>;
859
890
  readonly includeFilesystemTools?: boolean;
860
891
  readonly includeUploadTools?: boolean;
861
892
  readonly sessionDir?: string;
@@ -963,6 +994,8 @@ interface CreateAgentHostOptions {
963
994
  readonly fleetCompiler: AgentFleetCompiler;
964
995
  readonly hostId?: string;
965
996
  readonly scopeVerifier: AgentScopeVerifier;
997
+ /** Optional product-owned Seat/entitlement policy; omission preserves legacy fleet-wide access. */
998
+ readonly resolveAgentAccess?: ResolveAgentAccess;
966
999
  readonly runtimeModeAdapter: RuntimeModeAdapter;
967
1000
  readonly runtimeHost?: AgentRuntimeHostOperations;
968
1001
  readonly sessionRoot?: string;
@@ -1005,4 +1038,4 @@ interface CreatedAgentHost {
1005
1038
  runWithWorkspaceAgent(input: AgentHostDispatcherRunInput, run: (binding: LeaseBoundWorkspaceAgent) => Promise<void>): Promise<void>;
1006
1039
  }
1007
1040
 
1008
- export { PI_PACKAGE_RESOURCE_FILTERS as $, type AgentGatewayEffect as A, type BuiltinRuntimeModeId as B, type ConfiguredAgentHostAgentSpec as C, type AgentRequestLedgerRecord as D, type AgentFleetCompiler as E, type AgentRequestTarget as F, type RuntimeFilesystemAccessDecision as G, type AgentHostDescription as H, type AgentHostDirectProjectionOptions as I, type AgentHostDispatcherRunInput as J, type AgentHostEnvironmentLease as K, type AgentHostHandle as L, type ModeContext as M, type LegacyDefaultAgentHostSpec as N, type MeteringReleaseInput as O, type ProvisionWorkspaceRuntimeOptions as P, type MeteringReleaseReason as Q, type RuntimeModeAdapter as R, type MeteringReservationResult as S, type MeteringReserveInput as T, type MeteringRunKind as U, type MeteringRunScope as V, type WorkspaceProvisioningResult as W, type MeteringRunStatus as X, type MeteringSettleInput as Y, type MeteringUsage as Z, type MeteringUsageInput as _, type PiPackageSource as a, type PiExtensionFactory as a0, type PluginSkillSource as a1, type ResolvedEnvironmentScope as a2, type RuntimeFilesystemBindingOperations as a3, type RuntimeNodePackageSpec as a4, type RuntimeProvisioningContribution as a5, type RuntimePythonSpec as a6, type RuntimeTemplateContribution as a7, type WorkspaceProvisioningExecResult as a8, compactPiPackages as a9, mergePiPackageSources as aa, normalizeMeteringUsage as ab, piPackageSourceKey as ac, type RuntimeModeId as b, type AgentRuntimeHostOperations as c, type RuntimeBashStrategy as d, type RuntimeFilesystemStrategy as e, type WorkspaceProvisioningAdapter as f, type AgentInstructionSource as g, type AgentInstructionFileRef as h, type AgentHostAgentSpec as i, type RuntimeBundle as j, type PiHarnessOptions as k, type AgentMeteringSink as l, type RuntimeFilesystemBinding as m, type CreatedAgentHost as n, type MeteringErrorLogger as o, ReadyStatusTracker as p, type CreateAgentHostOptions as q, type CompiledAgentHostAgentSpec as r, type AgentRequestLedger as s, type AgentEffectAdmission as t, type AuthorizedEnvironmentIntent as u, type AgentHostEnvironmentScope as v, type ResolvedAgentRuntimeScope as w, type AgentRequestKey as x, type AgentRequestLedgerPrepareResult as y, type AgentRequestFailure as z };
1041
+ export { type MeteringUsageInput as $, type AgentGatewayEffect as A, type BuiltinRuntimeModeId as B, type ConfiguredAgentHostAgentSpec as C, type AgentRequestLedgerRecord as D, type AgentFleetCompiler as E, type AgentRequestTarget as F, type RuntimeFilesystemAccessDecision as G, type AgentHostDescription as H, type AgentHostDirectProjectionOptions as I, type AgentHostDispatcherRunInput as J, type AgentHostEnvironmentLease as K, type AgentHostHandle as L, type ModeContext as M, DEFAULT_AGENT_TYPE_ID as N, type MeteringErrorLogger as O, type ProvisionWorkspaceRuntimeOptions as P, type MeteringReleaseInput as Q, type RuntimeModeAdapter as R, type MeteringReleaseReason as S, type MeteringReservationResult as T, type MeteringReserveInput as U, type MeteringRunKind as V, type WorkspaceProvisioningResult as W, type MeteringRunScope as X, type MeteringRunStatus as Y, type MeteringSettleInput as Z, type MeteringUsage as _, type PiPackageSource as a, PI_PACKAGE_RESOURCE_FILTERS as a0, type PiExtensionFactory as a1, type PluginSkillSource as a2, type ResolvedEnvironmentScope as a3, type RuntimeFilesystemBindingOperations as a4, type RuntimeNodePackageSpec as a5, type RuntimeProvisioningContribution as a6, type RuntimePythonSpec as a7, type RuntimeTemplateContribution as a8, type WorkspaceProvisioningExecResult as a9, compactPiPackages as aa, mergePiPackageSources as ab, normalizeMeteringUsage as ac, piPackageSourceKey as ad, type RuntimeModeId as b, type AgentRuntimeHostOperations as c, type RuntimeBashStrategy as d, type RuntimeFilesystemStrategy as e, type WorkspaceProvisioningAdapter as f, type AgentInstructionSource as g, type AgentInstructionFileRef as h, type AgentHostAgentSpec as i, type RuntimeBundle as j, type PiHarnessOptions as k, type AgentMeteringSink as l, type RuntimeFilesystemBinding as m, type CreatedAgentHost as n, type ResolvedPiHarnessOptions as o, ReadyStatusTracker as p, type CreateAgentHostOptions as q, type CompiledAgentHostAgentSpec as r, type AgentRequestLedger as s, type AgentEffectAdmission as t, type AuthorizedEnvironmentIntent as u, type AgentHostEnvironmentScope as v, type ResolvedAgentRuntimeScope as w, type AgentRequestKey as x, type AgentRequestLedgerPrepareResult as y, type AgentRequestFailure as z };
@@ -33,16 +33,18 @@ Only the app-owned verifier returns identity facts, as
33
33
  `VerifiedAgentScopeClaim`. `AgentSessionRef` deliberately carries **no
34
34
  `hostId`** — session refs are `{ agentTypeId, sessionId }` and nothing else.
35
35
 
36
- ## The 7 methods (+ close)
36
+ ## The 8 methods (+ close, plus optional authorization hook)
37
37
 
38
38
  ```ts
39
39
  interface AgentGateway {
40
+ authorizeAgentAccess?(input: { scope; agentTypeId; operation }): Promise<void>
40
41
  listAgents(input: ListAgentsInput): Promise<readonly AgentSummary[]>
41
42
  listSessions(input: AuthorizedAgentSessionQuery): Promise<AgentSessionPage>
42
43
  createSession(input: CreateAgentSessionInput): Promise<AgentSessionRef>
43
44
  connectSession(input: ConnectAgentSessionInput): Promise<AgentSessionConnection>
44
45
  readSessionState(input: ReadAgentSessionStateInput): Promise<AgentSessionStateSnapshot>
45
46
  renameSession(input: RenameAgentSessionInput): Promise<AgentSessionSummary>
47
+ setSessionArchived(input: SetAgentSessionArchivedInput): Promise<AgentSessionSummary>
46
48
  deleteSession(input: DeleteAgentSessionInput): Promise<void>
47
49
  close(): Promise<void>
48
50
  }
@@ -61,6 +63,7 @@ interface CreateAgentSessionInput { scope; agentTypeId; requestId; title? }
61
63
  interface ConnectAgentSessionInput { scope; ref; cursor? }
62
64
  interface ReadAgentSessionStateInput { scope; ref }
63
65
  interface RenameAgentSessionInput { scope; ref; requestId; title }
66
+ interface SetAgentSessionArchivedInput { scope; ref; requestId; archived }
64
67
  interface DeleteAgentSessionInput { scope; ref; requestId }
65
68
  ```
66
69
 
@@ -97,10 +100,12 @@ shorthand. Prompts add `displayContent?`, `model?`, `thinkingLevel?`,
97
100
 
98
101
  ## Error codes
99
102
 
100
- Fourteen, exhaustive, order-stable, exported as both a const map and
103
+ Seventeen, exhaustive, order-stable, exported as both a const map and
101
104
  `AGENT_GATEWAY_ERROR_CODES`:
102
105
 
103
106
  `AGENT_TYPE_UNKNOWN` · `AGENT_SESSION_NOT_FOUND` · `AGENT_SCOPE_DENIED` ·
107
+ `AGENT_ENTITLEMENT_REQUIRED` · `AGENT_ACCESS_FORBIDDEN` ·
108
+ `AGENT_ACCESS_POLICY_UNAVAILABLE` ·
104
109
  `AGENT_SESSION_REPLAY_GAP` · `AGENT_SESSION_CURSOR_AHEAD` ·
105
110
  `AGENT_SESSION_CURSOR_EXPIRED` · `AGENT_SESSION_CURSOR_INVALID` ·
106
111
  `AGENT_REQUEST_CONFLICT` · `AGENT_REQUEST_IN_PROGRESS` ·
@@ -152,3 +157,36 @@ rejected. Future internal/external host tiers belong to #905, not here.
152
157
 
153
158
  `scripts/check-alignment-invariants.mjs` enforces that nothing outside an
154
159
  allowlist calls it.
160
+
161
+ ## Private backend seam
162
+
163
+ The session-runtime path below `EmbeddedAgentGateway` is the server-only
164
+ `AgentHarnessBackend`. It is private to the `createAgentHost()` construction
165
+ funnel: consumers still inject only `harnessFactory`, and there is no public
166
+ backend factory or second session path.
167
+
168
+ `AgentPromptPayload` is exposed from the shared chat barrel as a type only. Its
169
+ “server-only” contract means browser schemas never accept the `requireIdle`
170
+ selector.
171
+
172
+ `PiSessionHarnessBackend` is the sole production implementation today. It
173
+ adapts the unchanged `HarnessPiChatService` and owns the mapping from the
174
+ workspace-scoped backend address and attribution context into the legacy Pi
175
+ request context. The backend carries no credentials, membership authority, or
176
+ model catalog. Reload and slash-command host effects continue to use the
177
+ composition harness directly; they are host capabilities, not session-runtime
178
+ operations.
179
+
180
+ The Gateway request ledger is the only idempotency authority. A backend method
181
+ may be invoked again after a crash only through a ledger-admitted request; the
182
+ backend itself promises no deduplication. P1-B substitutes the adapter-private
183
+ replay source below `HarnessPiChatService`, not this interface. Later operations
184
+ may extend the interface only in their named slices (`resumePausedToolCall` in
185
+ A3b and `markTurnInterrupted` in A4); neither is part of the current seam.
186
+
187
+ CI pins the boundary in three directions: Agent Host production code cannot
188
+ import a Pi runtime, only `harnessBackend/**` can reference the concrete Pi chat
189
+ service, and Workspace/Core/CLI/playground consumers cannot name the private
190
+ backend interface or module path. Retire the legacy `PiChatSessionService` when
191
+ the legacy `core/createAgent` path is removed; it is intentionally not widened
192
+ or migrated here.
@@ -98,6 +98,7 @@ All API failures must use the response envelope:
98
98
  | `ABORTED` | Request cancelled via `AbortSignal` | 499 | retry | warn | stable (public API) |
99
99
  | `PAYMENT_REQUIRED` | Billing/metering sink rejected the run (e.g. credits exhausted) | 402 | user-fix | warn | stable (public API) |
100
100
  | `MODEL_BUDGET_EXCEEDED` | Governance model budget for this user/model is exhausted | 402 | user-fix | warn | stable (public API) |
101
+ | `MODEL_CONTEXT_WINDOW_EXCEEDED` | Provider rejects a model request because its conversation context is too large | 413 | compact/retry or start a smaller chat | warn | stable (public API) |
101
102
  | `METERING_UNSUPPORTED_COMMAND` | Slash-command execution is disabled because metering cannot yet reserve/settle that path | 409 | user-fix | warn | stable (public API) |
102
103
  | `SESSION_NOT_FOUND` | Session id does not exist | 404 | user-fix | warn | stable (public API) |
103
104
  | `SESSION_LOCKED` | Session currently locked by concurrent writer | 409 | retry | warn | stable (public API) |
package/docs/README.md CHANGED
@@ -82,6 +82,7 @@ the agent ↔ workspace boundary is in
82
82
 
83
83
  **Contracts & operations**
84
84
 
85
+ - [SESSION_IDENTITY](./SESSION_IDENTITY.md) — durable session stream key, owner attributes, and v1→v2 migration contract.
85
86
  - [ERROR_CODES](./ERROR_CODES.md) — the stable API error-code registry.
86
87
  - [CSP](./CSP.md) — Content-Security-Policy compatibility.
87
88
  - [ACCESSIBILITY](./ACCESSIBILITY.md) — a11y coverage and known gaps.
@@ -0,0 +1,91 @@
1
+ # Session stream identity
2
+
3
+ This contract governs durable agent event-stream addressing. The code in
4
+ `src/shared/events.ts` is authoritative when this document and the
5
+ implementation disagree.
6
+
7
+ ## Durable key
8
+
9
+ A session stream is keyed by `(workspaceScopeId, sessionId)`. Its path is:
10
+
11
+ ```text
12
+ sessions/<enc(workspaceScopeId)>/<enc(sessionId)>
13
+ ```
14
+
15
+ `enc` percent-encodes every byte outside `[A-Za-z0-9._-]`, using uppercase
16
+ hex. Both fields are opaque, non-empty, well-formed Unicode strings; session
17
+ ids need not be UUIDs.
18
+ `sessionStreamPath(identity)` is the only constructor and
19
+ `parseSessionStreamPath(path)` accepts only the canonical encoding.
20
+ The value helpers are server-internal and are not exported from the package's
21
+ shared public barrel; only the `SessionStreamIdentity` type is public there.
22
+
23
+ The process-local Pi cache may still use a JSON tuple, but it is not a durable
24
+ key. Gateway host maps use `(workspaceScopeId, agentTypeId, sessionId)`, and
25
+ Pi transcript directories use their existing agent and workspace namespace.
26
+ Neither grammar may be embedded into a durable stream path.
27
+
28
+ When no event store is configured, legacy/headless callers may omit
29
+ `workspaceId`. The service preserves their pre-durability JSON cache key and
30
+ never constructs or validates a durable path. Once an event store is present,
31
+ both identity fields are required and invalid identity or durable-read errors
32
+ surface instead of being treated as a missing transcript.
33
+
34
+ ## Owner attributes
35
+
36
+ `boring_event_stream_owners` stores one row for each addressed session stream:
37
+
38
+ - `path` is the primary key.
39
+ - `workspace_scope_id` and `session_id` repeat the durable identity and are
40
+ indexed together.
41
+ - `agent_type_id` is required for newly opened channels but is an attribute,
42
+ not part of the stream key.
43
+ - `auth_subject_id` is execution attribution, never ownership.
44
+ - `seat_id` and `thread_id` are reserved nullable columns; A1 defines no
45
+ semantics or foreign keys for them.
46
+ - `key_version` is `2` for streams created with this grammar and `1` for
47
+ migrated rows.
48
+
49
+ There is deliberately no foreign key. SQLite foreign-key enforcement is not
50
+ enabled on this connection; migration validation and store transactions enforce
51
+ the relationship instead.
52
+
53
+ `auth_subject_id` uses first-writer semantics: the first successful
54
+ `createSessionStream` claim records it, and later opens do not replace it.
55
+ This is attribution for the opening execution, not a durable authorization or
56
+ ownership decision.
57
+
58
+ ## Schema v1 to v2
59
+
60
+ Opening a v1 database runs one `BEGIN IMMEDIATE` migration. It preflights every
61
+ old stream into `boring_event_stream_migration_v2` by parent `rowid`, raw
62
+ `path`, and `typeof(path)`, validates unique targets, every parent's path and
63
+ metadata, per-stream row counts and maximum sequence values, every owner row,
64
+ and every child row before swapping paths across all three event tables.
65
+
66
+ The collision rules are exhaustive:
67
+
68
+ 1. A single old row rekeys to its v2 path.
69
+ 2. For an empty-user row colliding with user-bearing rows, the empty-user row
70
+ wins and the others are quarantined.
71
+ 3. If several user-bearing rows collide without an empty-user row, all are
72
+ quarantined because their sequence spaces cannot be merged safely.
73
+ 4. Non-matching, malformed, or otherwise ambiguous old paths are quarantined.
74
+
75
+ Legacy identities containing ill-formed Unicode are quarantined with reason
76
+ `unencodable-identity`. Non-TEXT parent paths (including SQL `NULL`) use a
77
+ rowid-safe quarantine destination; their raw original value/type remains in
78
+ the manifest. No encoding failure aborts migration of the remaining rows.
79
+
80
+ Ordinary quarantine destinations are `quarantine/v1/<enc(oldPath)>`; rowid-safe
81
+ destinations cover values that cannot be encoded. Rows are never dropped. The
82
+ swap is validated again before `schema_version` becomes `2`.
83
+ Concurrent openers serialize on the immediate transaction, re-read the version
84
+ under the lock, and only one migrates. A migrated owner has
85
+ `agent_type_id = NULL` until the next legitimate channel open backfills it from
86
+ the composition's explicit agent type.
87
+
88
+ The migration is a rollback fence, not a reversible downgrade. Disable
89
+ `BORING_CHAT_DURABLE_STREAM` before running an old binary; an old binary pointed
90
+ at a v2 file refuses it with `EventStreamSchemaVersionError` rather than writing
91
+ mixed path grammars.
package/docs/tools.md CHANGED
@@ -93,6 +93,10 @@ resources declared in `package.json#pi` (`extensions`, `skills`, `prompts`, and
93
93
  place for tools/skills that should update without restarting the workspace
94
94
  server.
95
95
 
96
+ ## Optional trusted plugin tools
97
+
98
+ App hosts may add tools through trusted Workspace server plugins. Disposable lease support is additive: the optional sandbox plugin supplies `sandbox` and `sandbox_bash` without changing canonical bash or file-tool schemas. Agent-authored plugin selection alone cannot grant that host authority.
99
+
96
100
  ## Runtime modes
97
101
 
98
102
  See [runtime.md](./runtime.md) for how tool execution is sandboxed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-agent",
3
- "version": "0.1.105",
3
+ "version": "0.1.107",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Pane-embeddable coding agent. Ships direct/local/vercel-sandbox execution modes behind one interface.",
@@ -69,7 +69,7 @@
69
69
  "docs"
70
70
  ],
71
71
  "dependencies": {
72
- "@mariozechner/pi-coding-agent": "npm:@earendil-works/pi-coding-agent@0.80.7",
72
+ "@mariozechner/pi-coding-agent": "npm:@earendil-works/pi-coding-agent@0.84.3",
73
73
  "@modelcontextprotocol/sdk": "1.30.0",
74
74
  "@radix-ui/react-collapsible": "^1.1.20",
75
75
  "@radix-ui/react-dialog": "^1.1.23",
@@ -85,12 +85,12 @@
85
85
  "@streamdown/math": "^1.0.2",
86
86
  "@streamdown/mermaid": "^1.0.2",
87
87
  "@tanstack/react-query": "^5.101.4",
88
- "ai": "^7.0.65",
88
+ "ai": "^7.0.68",
89
89
  "class-variance-authority": "^0.7.1",
90
90
  "clsx": "^2.1.1",
91
91
  "cmdk": "^1.1.1",
92
92
  "diff": "^9.0.0",
93
- "fastify": "^5.12.0",
93
+ "fastify": "^5.12.1",
94
94
  "lucide-react": "^1.31.0",
95
95
  "motion": "^13.1.0",
96
96
  "nanoid": "^6.0.0",
@@ -100,9 +100,9 @@
100
100
  "use-stick-to-bottom": "^1.1.6",
101
101
  "yaml": "^2.9.0",
102
102
  "zod": "^3.25.76",
103
- "@hachej/boring-bash": "0.1.105",
104
- "@hachej/boring-sandbox": "0.1.105",
105
- "@hachej/boring-ui-kit": "0.1.105"
103
+ "@hachej/boring-bash": "0.1.107",
104
+ "@hachej/boring-sandbox": "0.1.107",
105
+ "@hachej/boring-ui-kit": "0.1.107"
106
106
  },
107
107
  "devDependencies": {
108
108
  "@vercel/sandbox": "3.0.0",
@@ -122,13 +122,14 @@
122
122
  "tsx": "^4.23.6",
123
123
  "typescript": "^6.0.3",
124
124
  "vite": "^8.2.1",
125
- "vitest": "^4.1.9"
125
+ "@earendil-works/pi-ai": "0.84.3",
126
+ "vitest": "^4.1.11"
126
127
  },
127
128
  "peerDependencies": {
128
129
  "react": "^18.0.0 || ^19.0.0",
129
130
  "react-dom": "^18.0.0 || ^19.0.0",
130
131
  "tailwindcss": ">=3.4.0 || >=4.0.0",
131
- "vitest": "^4.1.9"
132
+ "vitest": "^4.1.11"
132
133
  },
133
134
  "peerDependenciesMeta": {
134
135
  "react": {
@@ -145,7 +146,7 @@
145
146
  }
146
147
  },
147
148
  "scripts": {
148
- "build": "tsup && node ./scripts/build-front-css.mjs && pnpm run build:assert",
149
+ "build": "tsup && pnpm run build:assert",
149
150
  "build:dev": "{ [ -d '../ui/dist' ] || pnpm --filter @hachej/boring-ui-kit build; } && tsup --config tsup.dev.config.ts && node ./scripts/build-front-css.mjs",
150
151
  "build:assert": "node ./scripts/assert-build-artifacts.mjs",
151
152
  "dev": "tsx src/server/dev.ts",
@@ -1,18 +0,0 @@
1
- // src/shared/events.ts
2
- var AGENT_NOT_IMPLEMENTED_UNTIL_T1 = "ERR_NOT_IMPLEMENTED_UNTIL_T1";
3
- function sessionStreamPath(sessionId) {
4
- return `sessions/${sessionId}`;
5
- }
6
- var AgentNotImplementedError = class extends Error {
7
- code = AGENT_NOT_IMPLEMENTED_UNTIL_T1;
8
- constructor(message = "This agent capability is not implemented until T1.") {
9
- super(message);
10
- this.name = "AgentNotImplementedError";
11
- }
12
- };
13
-
14
- export {
15
- AGENT_NOT_IMPLEMENTED_UNTIL_T1,
16
- sessionStreamPath,
17
- AgentNotImplementedError
18
- };