@xenosystem/agent-interface-host 0.1.4 → 0.1.6

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 (124) hide show
  1. package/dist/acp/AcpAgentRegistryService.d.ts +43 -1
  2. package/dist/acp/AcpAgentRegistryService.d.ts.map +1 -1
  3. package/dist/acp/AcpAgentRegistryService.js +200 -1
  4. package/dist/acp/AcpAgentRegistryService.js.map +1 -1
  5. package/dist/acp/AcpAgentTurnExecutionAdapter.d.ts +66 -0
  6. package/dist/acp/AcpAgentTurnExecutionAdapter.d.ts.map +1 -1
  7. package/dist/acp/AcpAgentTurnExecutionAdapter.js +182 -1
  8. package/dist/acp/AcpAgentTurnExecutionAdapter.js.map +1 -1
  9. package/dist/acp/AcpPermissionBridge.d.ts.map +1 -1
  10. package/dist/acp/AcpPermissionBridge.js +3 -3
  11. package/dist/acp/AcpPermissionBridge.js.map +1 -1
  12. package/dist/acp/AcpProcessSupervisor.d.ts +81 -0
  13. package/dist/acp/AcpProcessSupervisor.d.ts.map +1 -1
  14. package/dist/acp/AcpProcessSupervisor.js +176 -2
  15. package/dist/acp/AcpProcessSupervisor.js.map +1 -1
  16. package/dist/acp/AcpProviderCatalogService.d.ts.map +1 -1
  17. package/dist/acp/AcpProviderCatalogService.js +68 -2
  18. package/dist/acp/AcpProviderCatalogService.js.map +1 -1
  19. package/dist/acp/AcpRuntimeEventMapper.d.ts.map +1 -1
  20. package/dist/acp/AcpRuntimeEventMapper.js +9 -0
  21. package/dist/acp/AcpRuntimeEventMapper.js.map +1 -1
  22. package/dist/acp/AcpSessionService.d.ts +36 -0
  23. package/dist/acp/AcpSessionService.d.ts.map +1 -1
  24. package/dist/acp/AcpSessionService.js +57 -1
  25. package/dist/acp/AcpSessionService.js.map +1 -1
  26. package/dist/acp/DurableAcpPermissionPolicyStore.d.ts.map +1 -1
  27. package/dist/acp/DurableAcpPermissionPolicyStore.js +5 -3
  28. package/dist/acp/DurableAcpPermissionPolicyStore.js.map +1 -1
  29. package/dist/acp/acpRedaction.d.ts.map +1 -1
  30. package/dist/acp/acpRedaction.js +2 -0
  31. package/dist/acp/acpRedaction.js.map +1 -1
  32. package/dist/acp/acpTypes.d.ts +111 -1
  33. package/dist/acp/acpTypes.d.ts.map +1 -1
  34. package/dist/acp/acpTypes.js +21 -1
  35. package/dist/acp/acpTypes.js.map +1 -1
  36. package/dist/index.d.ts +108 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +236 -11
  39. package/dist/index.js.map +1 -1
  40. package/dist/isolation/CommandExecution.d.ts +7 -12
  41. package/dist/isolation/CommandExecution.d.ts.map +1 -1
  42. package/dist/isolation/CommandExecution.js +7 -12
  43. package/dist/isolation/CommandExecution.js.map +1 -1
  44. package/dist/isolation/CommandStream.d.ts +100 -0
  45. package/dist/isolation/CommandStream.d.ts.map +1 -0
  46. package/dist/isolation/CommandStream.js +47 -0
  47. package/dist/isolation/CommandStream.js.map +1 -0
  48. package/dist/isolation/RunExecutionEnvironment.d.ts +15 -0
  49. package/dist/isolation/RunExecutionEnvironment.d.ts.map +1 -1
  50. package/dist/isolation/RunExecutionEnvironment.js +45 -9
  51. package/dist/isolation/RunExecutionEnvironment.js.map +1 -1
  52. package/dist/isolation/RunIsolationService.d.ts +10 -5
  53. package/dist/isolation/RunIsolationService.d.ts.map +1 -1
  54. package/dist/isolation/RunIsolationService.js +7 -3
  55. package/dist/isolation/RunIsolationService.js.map +1 -1
  56. package/dist/isolation/SandboxCapabilities.d.ts +28 -0
  57. package/dist/isolation/SandboxCapabilities.d.ts.map +1 -0
  58. package/dist/isolation/SandboxCapabilities.js +33 -0
  59. package/dist/isolation/SandboxCapabilities.js.map +1 -0
  60. package/dist/isolation/SandboxCommandExecutor.d.ts +4 -7
  61. package/dist/isolation/SandboxCommandExecutor.d.ts.map +1 -1
  62. package/dist/isolation/SandboxCommandExecutor.js +6 -7
  63. package/dist/isolation/SandboxCommandExecutor.js.map +1 -1
  64. package/dist/isolation/SandboxCommandStream.d.ts +38 -0
  65. package/dist/isolation/SandboxCommandStream.d.ts.map +1 -0
  66. package/dist/isolation/SandboxCommandStream.js +232 -0
  67. package/dist/isolation/SandboxCommandStream.js.map +1 -0
  68. package/dist/isolation/shellArgv.d.ts +15 -0
  69. package/dist/isolation/shellArgv.d.ts.map +1 -0
  70. package/dist/isolation/shellArgv.js +31 -0
  71. package/dist/isolation/shellArgv.js.map +1 -0
  72. package/dist/isolation/useIdempotency.d.ts +19 -0
  73. package/dist/isolation/useIdempotency.d.ts.map +1 -0
  74. package/dist/isolation/useIdempotency.js +57 -0
  75. package/dist/isolation/useIdempotency.js.map +1 -0
  76. package/dist/localRpcTransport.d.ts +18 -0
  77. package/dist/localRpcTransport.d.ts.map +1 -1
  78. package/dist/localRpcTransport.js +38 -4
  79. package/dist/localRpcTransport.js.map +1 -1
  80. package/dist/node.d.ts +19 -0
  81. package/dist/node.d.ts.map +1 -1
  82. package/dist/node.js +19 -0
  83. package/dist/node.js.map +1 -1
  84. package/dist/observation/XenoUseRunObserver.d.ts +20 -0
  85. package/dist/observation/XenoUseRunObserver.d.ts.map +1 -1
  86. package/dist/observation/XenoUseRunObserver.js +27 -13
  87. package/dist/observation/XenoUseRunObserver.js.map +1 -1
  88. package/dist/runtime/RuntimeTimelineProjection.d.ts +2 -2
  89. package/dist/runtime/RuntimeTimelineProjection.d.ts.map +1 -1
  90. package/dist/runtime/RuntimeTimelineProjection.js +196 -1
  91. package/dist/runtime/RuntimeTimelineProjection.js.map +1 -1
  92. package/dist/sdk/DurablePendingInjectionStore.d.ts +24 -0
  93. package/dist/sdk/DurablePendingInjectionStore.d.ts.map +1 -0
  94. package/dist/sdk/DurablePendingInjectionStore.js +103 -0
  95. package/dist/sdk/DurablePendingInjectionStore.js.map +1 -0
  96. package/dist/sdk/SdkNativeAgentTurnExecutionAdapter.d.ts +81 -1
  97. package/dist/sdk/SdkNativeAgentTurnExecutionAdapter.d.ts.map +1 -1
  98. package/dist/sdk/SdkNativeAgentTurnExecutionAdapter.js +122 -3
  99. package/dist/sdk/SdkNativeAgentTurnExecutionAdapter.js.map +1 -1
  100. package/dist/sdk/pendingInjections.d.ts +83 -0
  101. package/dist/sdk/pendingInjections.d.ts.map +1 -0
  102. package/dist/sdk/pendingInjections.js +79 -0
  103. package/dist/sdk/pendingInjections.js.map +1 -0
  104. package/dist/session/AgentSessionService.d.ts +34 -0
  105. package/dist/session/AgentSessionService.d.ts.map +1 -1
  106. package/dist/session/AgentSessionService.js +85 -1
  107. package/dist/session/AgentSessionService.js.map +1 -1
  108. package/dist/session/nativeTuiSessionPty.d.ts +40 -0
  109. package/dist/session/nativeTuiSessionPty.d.ts.map +1 -0
  110. package/dist/session/nativeTuiSessionPty.js +142 -0
  111. package/dist/session/nativeTuiSessionPty.js.map +1 -0
  112. package/dist/session/nodePtyLoader.d.ts +23 -0
  113. package/dist/session/nodePtyLoader.d.ts.map +1 -0
  114. package/dist/session/nodePtyLoader.js +41 -0
  115. package/dist/session/nodePtyLoader.js.map +1 -0
  116. package/dist/session/renderSupport.d.ts +34 -0
  117. package/dist/session/renderSupport.d.ts.map +1 -1
  118. package/dist/session/renderSupport.js +39 -1
  119. package/dist/session/renderSupport.js.map +1 -1
  120. package/dist/workspace/WorkspaceHostService.d.ts +27 -2
  121. package/dist/workspace/WorkspaceHostService.d.ts.map +1 -1
  122. package/dist/workspace/WorkspaceHostService.js +221 -25
  123. package/dist/workspace/WorkspaceHostService.js.map +1 -1
  124. package/package.json +8 -8
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nativeTuiSessionPty.js","sourceRoot":"","sources":["../../src/session/nativeTuiSessionPty.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The one place `node-pty` is loaded.
3
+ *
4
+ * Lazy and failure-tolerant on purpose: it is a native module, it is absent in
5
+ * a headless or web composition, and a host that cannot load it must still
6
+ * serve every other method rather than fail to construct.
7
+ *
8
+ * 🔴 Extracted rather than copied. `WorkspaceHostService` had this logic
9
+ * inline, and the `native-tui` session PTY needs exactly the same thing — two
10
+ * copies of "how do we load the native terminal, and what do we say when we
11
+ * cannot" is how the two come to disagree about whether a terminal is
12
+ * available.
13
+ */
14
+ export type NodePtyModule = typeof import('node-pty');
15
+ export interface NodePtyLoad {
16
+ module: NodePtyModule | null;
17
+ /** Why not, when not — never a bare null. */
18
+ error: string | null;
19
+ }
20
+ export declare function loadNodePty(): Promise<NodePtyLoad>;
21
+ /** Test seam. Never called in production — the cache is process-lifetime. */
22
+ export declare function resetNodePtyLoaderForTests(): void;
23
+ //# sourceMappingURL=nodePtyLoader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodePtyLoader.d.ts","sourceRoot":"","sources":["../../src/session/nodePtyLoader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,aAAa,GAAG,cAAc,UAAU,CAAC,CAAA;AAErD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IAC5B,6CAA6C;IAC7C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAID,wBAAgB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAqBlD;AAED,6EAA6E;AAC7E,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The one place `node-pty` is loaded.
3
+ *
4
+ * Lazy and failure-tolerant on purpose: it is a native module, it is absent in
5
+ * a headless or web composition, and a host that cannot load it must still
6
+ * serve every other method rather than fail to construct.
7
+ *
8
+ * 🔴 Extracted rather than copied. `WorkspaceHostService` had this logic
9
+ * inline, and the `native-tui` session PTY needs exactly the same thing — two
10
+ * copies of "how do we load the native terminal, and what do we say when we
11
+ * cannot" is how the two come to disagree about whether a terminal is
12
+ * available.
13
+ */
14
+ let cached;
15
+ export function loadNodePty() {
16
+ if (!cached) {
17
+ cached = import('node-pty')
18
+ .then((loaded) => {
19
+ const namespace = loaded;
20
+ const module = (namespace.default || namespace);
21
+ // Present-but-wrong is a real case: a stub, a browser shim, or a build
22
+ // that resolved the package without its native binding.
23
+ if (typeof module.spawn !== 'function') {
24
+ throw new Error('node-pty did not expose a spawn function.');
25
+ }
26
+ return { module, error: null };
27
+ })
28
+ .catch((error) => ({
29
+ module: null,
30
+ error: error instanceof Error && error.message.trim()
31
+ ? error.message.slice(0, 1_000)
32
+ : 'Failed to load node-pty.',
33
+ }));
34
+ }
35
+ return cached;
36
+ }
37
+ /** Test seam. Never called in production — the cache is process-lifetime. */
38
+ export function resetNodePtyLoaderForTests() {
39
+ cached = undefined;
40
+ }
41
+ //# sourceMappingURL=nodePtyLoader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodePtyLoader.js","sourceRoot":"","sources":["../../src/session/nodePtyLoader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAUH,IAAI,MAAwC,CAAA;AAE5C,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;aACxB,IAAI,CAAC,CAAC,MAAe,EAAE,EAAE;YACxB,MAAM,SAAS,GAAG,MAA8D,CAAA;YAChF,MAAM,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,IAAI,SAAS,CAAkB,CAAA;YAChE,uEAAuE;YACvE,wDAAwD;YACxD,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBACvC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;YAC9D,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;QAChC,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC;YAC1B,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;gBACnD,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;gBAC/B,CAAC,CAAC,0BAA0B;SAC/B,CAAC,CAAC,CAAA;IACP,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,0BAA0B;IACxC,MAAM,GAAG,SAAS,CAAA;AACpB,CAAC"}
@@ -15,6 +15,22 @@ import type { AgentRenderSupport } from '@xenosystem/agent-interface-core';
15
15
  * PTY lane is built it replaces this function, and `renderSupportFor`'s tests
16
16
  * already prove a `true` answer flows through — so the day it lands, the mode
17
17
  * becomes available by construction instead of by someone remembering.
18
+ *
19
+ * ⚠️ That claim was NOT true until 2026-08-24, and the way it was false is
20
+ * worth keeping. Construction reached as far as this file and stopped one
21
+ * caller short, in two places at once:
22
+ *
23
+ * 1. `AgentHostCoordinator` never accepted a `nativeTuiLane`, so no
24
+ * composition could reach the seam. Injectable and unreachable.
25
+ * 2. `openSessionForRun` built the ACP protocol channel UNCONDITIONALLY,
26
+ * and `open()` refuses `native-tui` when one is present — so a lane
27
+ * returning true would have made every session on that provider fail
28
+ * with `protocol_channel_forbidden`.
29
+ *
30
+ * Both are fixed and mutation-checked at the caller. The lesson generalises:
31
+ * a seam proves a value can flow THROUGH the function that declares it, never
32
+ * that anything calls that function correctly. `renderSupport.test.ts` proved
33
+ * the first and read as proving the second.
18
34
  */
19
35
  export declare function noNativeTuiLane(): boolean;
20
36
  export interface RenderSupportOptions {
@@ -48,6 +64,24 @@ export interface RenderSupportOptions {
48
64
  * A provider no adapter supports gets `{ nativeTui: false, acp: false }`, and
49
65
  * `selectSessionRenderMode` REFUSES it by name. That is the honest outcome: the
50
66
  * alternative is a session that opens, renders nothing, and reports no error.
67
+ *
68
+ * 🔴 AND BEING BLOCKED COUNTS AS "cannot run" — measured 2026-08-24.
69
+ *
70
+ * `AcpAgentTurnExecutionAdapter.supports()` is `provider.kind === 'acp'`. It
71
+ * answers "is this the right KIND", not "can this actually run", so a blocked
72
+ * endpoint reported `acp: true` and, with a TUI present, `switchable: true` —
73
+ * a toggle that switched into an empty terminal.
74
+ *
75
+ * Latent until now, because a blocked provider cannot start a turn and no
76
+ * session was ever opened for one. It stops being latent the moment a TUI-only
77
+ * agent is registered — Grok Build, agy and opencode have no ACP adapter in
78
+ * `xeno-acp`, so they arrive blocked AND with a real interface, which is
79
+ * exactly the combination that had no honest answer.
80
+ *
81
+ * Fixed HERE rather than in `supports()`: that predicate also routes turn
82
+ * EXECUTION, and narrowing it would change which adapter is found for a run
83
+ * that is refused for other reasons. This question — "can we render this
84
+ * agent's structured stream" — is the one that needs the stricter answer.
51
85
  */
52
86
  export declare function renderSupportFor(provider: AgentRuntimeProviderDescriptor, options: RenderSupportOptions): AgentRenderSupport;
53
87
  //# sourceMappingURL=renderSupport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderSupport.d.ts","sourceRoot":"","sources":["../../src/session/renderSupport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAA;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAE1E;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;OAUG;IACH,QAAQ,EAAE,SAAS;QAAE,QAAQ,CAAC,QAAQ,EAAE,8BAA8B,GAAG,OAAO,CAAA;KAAE,EAAE,CAAA;IACpF,+EAA+E;IAC/E,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,8BAA8B,KAAK,OAAO,CAAA;CACtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,8BAA8B,EACxC,OAAO,EAAE,oBAAoB,GAC5B,kBAAkB,CAMpB"}
1
+ {"version":3,"file":"renderSupport.d.ts","sourceRoot":"","sources":["../../src/session/renderSupport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAA;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;OAUG;IACH,QAAQ,EAAE,SAAS;QAAE,QAAQ,CAAC,QAAQ,EAAE,8BAA8B,GAAG,OAAO,CAAA;KAAE,EAAE,CAAA;IACpF,+EAA+E;IAC/E,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,8BAA8B,KAAK,OAAO,CAAA;CACtE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,8BAA8B,EACxC,OAAO,EAAE,oBAAoB,GAC5B,kBAAkB,CAUpB"}
@@ -13,6 +13,22 @@
13
13
  * PTY lane is built it replaces this function, and `renderSupportFor`'s tests
14
14
  * already prove a `true` answer flows through — so the day it lands, the mode
15
15
  * becomes available by construction instead of by someone remembering.
16
+ *
17
+ * ⚠️ That claim was NOT true until 2026-08-24, and the way it was false is
18
+ * worth keeping. Construction reached as far as this file and stopped one
19
+ * caller short, in two places at once:
20
+ *
21
+ * 1. `AgentHostCoordinator` never accepted a `nativeTuiLane`, so no
22
+ * composition could reach the seam. Injectable and unreachable.
23
+ * 2. `openSessionForRun` built the ACP protocol channel UNCONDITIONALLY,
24
+ * and `open()` refuses `native-tui` when one is present — so a lane
25
+ * returning true would have made every session on that provider fail
26
+ * with `protocol_channel_forbidden`.
27
+ *
28
+ * Both are fixed and mutation-checked at the caller. The lesson generalises:
29
+ * a seam proves a value can flow THROUGH the function that declares it, never
30
+ * that anything calls that function correctly. `renderSupport.test.ts` proved
31
+ * the first and read as proving the second.
16
32
  */
17
33
  export function noNativeTuiLane() {
18
34
  return false;
@@ -30,12 +46,34 @@ export function noNativeTuiLane() {
30
46
  * A provider no adapter supports gets `{ nativeTui: false, acp: false }`, and
31
47
  * `selectSessionRenderMode` REFUSES it by name. That is the honest outcome: the
32
48
  * alternative is a session that opens, renders nothing, and reports no error.
49
+ *
50
+ * 🔴 AND BEING BLOCKED COUNTS AS "cannot run" — measured 2026-08-24.
51
+ *
52
+ * `AcpAgentTurnExecutionAdapter.supports()` is `provider.kind === 'acp'`. It
53
+ * answers "is this the right KIND", not "can this actually run", so a blocked
54
+ * endpoint reported `acp: true` and, with a TUI present, `switchable: true` —
55
+ * a toggle that switched into an empty terminal.
56
+ *
57
+ * Latent until now, because a blocked provider cannot start a turn and no
58
+ * session was ever opened for one. It stops being latent the moment a TUI-only
59
+ * agent is registered — Grok Build, agy and opencode have no ACP adapter in
60
+ * `xeno-acp`, so they arrive blocked AND with a real interface, which is
61
+ * exactly the combination that had no honest answer.
62
+ *
63
+ * Fixed HERE rather than in `supports()`: that predicate also routes turn
64
+ * EXECUTION, and narrowing it would change which adapter is found for a run
65
+ * that is refused for other reasons. This question — "can we render this
66
+ * agent's structured stream" — is the one that needs the stricter answer.
33
67
  */
34
68
  export function renderSupportFor(provider, options) {
35
69
  const nativeTuiLane = options.nativeTuiLane ?? noNativeTuiLane;
36
70
  return {
37
71
  nativeTui: nativeTuiLane(provider),
38
- acp: options.adapters.some((adapter) => adapter.supports(provider)),
72
+ // Both halves, and `blocked` fails the second one: a run that cannot
73
+ // execute emits no runtime events, so there is no structured stream to
74
+ // render and claiming otherwise offers an empty terminal.
75
+ acp: provider.availability !== 'blocked'
76
+ && options.adapters.some((adapter) => adapter.supports(provider)),
39
77
  };
40
78
  }
41
79
  //# sourceMappingURL=renderSupport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderSupport.js","sourceRoot":"","sources":["../../src/session/renderSupport.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,CAAA;AACd,CAAC;AAmBD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAwC,EACxC,OAA6B;IAE7B,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,eAAe,CAAA;IAC9D,OAAO;QACL,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC;QAClC,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;KACpE,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"renderSupport.js","sourceRoot":"","sources":["../../src/session/renderSupport.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,CAAA;AACd,CAAC;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAwC,EACxC,OAA6B;IAE7B,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,eAAe,CAAA;IAC9D,OAAO;QACL,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC;QAClC,qEAAqE;QACrE,uEAAuE;QACvE,0DAA0D;QAC1D,GAAG,EAAE,QAAQ,CAAC,YAAY,KAAK,SAAS;eACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;KACpE,CAAA;AACH,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { type CommandExecutionPort } from '../isolation/CommandExecution.js';
2
+ import type { CommandStreamPort } from '../isolation/CommandStream.js';
2
3
  import type { AgentHostEventType, AgentHostClientIdentity, AgentWorkspaceAccessContext, AgentWorkspaceCommandRunRequest, AgentWorkspaceCommandRunResult, AgentWorkspaceDirectoryListRequest, AgentWorkspaceDirectoryListResult, AgentWorkspaceFileReadRequest, AgentWorkspaceFileReadResult, AgentWorkspaceFileWriteRequest, AgentWorkspaceFileWriteResult, AgentWorkspaceGrantRequest, AgentWorkspaceGrantResult, AgentWorkspacePtyInputRequest, AgentWorkspacePtyResizeRequest, AgentWorkspacePtySnapshotRequest, AgentWorkspacePtySnapshotResult, AgentWorkspacePtyStartRequest, AgentWorkspacePtyStartResult, AgentWorkspaceTerminalControlRequest, AgentWorkspaceTerminalControlResult, AgentWorkspaceTerminalStartRequest, AgentWorkspaceTerminalStartResult } from '@xenosystem/agent-interface-contract';
3
4
  type WorkspaceEventSink = (type: AgentHostEventType, payload: Record<string, unknown>) => void;
4
5
  interface PersistedApplicationGrant {
@@ -33,7 +34,21 @@ export interface WorkspaceHostServiceOptions {
33
34
  * which executor serves a conversation.
34
35
  */
35
36
  export interface RunCommandExecutionEnvironment {
36
- executorFor(conversationId: string | undefined): CommandExecutionPort;
37
+ /**
38
+ * Executor AND containment for this conversation's current run.
39
+ *
40
+ * 🔴 One lookup, not two. argv depends on the destination — a Linux sandbox
41
+ * cannot run `powershell.exe` — so a caller that asked for the executor and
42
+ * then guessed the shell would pick the host's argv the moment isolation
43
+ * turned on. See `commandShellArgv`.
44
+ */
45
+ resolutionForConversation(conversationId: string | undefined): {
46
+ executor: CommandExecutionPort;
47
+ isolation: {
48
+ isolation: string;
49
+ };
50
+ stream?: CommandStreamPort;
51
+ };
37
52
  }
38
53
  export interface WorkspacePathAuthorizationRequest extends AgentWorkspaceAccessContext {
39
54
  path: string;
@@ -102,7 +117,7 @@ export declare class WorkspaceHostService {
102
117
  readFile(request: AgentWorkspaceFileReadRequest): AgentWorkspaceFileReadResult;
103
118
  writeFile(request: AgentWorkspaceFileWriteRequest): AgentWorkspaceFileWriteResult;
104
119
  runCommand(request: AgentWorkspaceCommandRunRequest): Promise<AgentWorkspaceCommandRunResult>;
105
- startTerminal(request: AgentWorkspaceTerminalStartRequest): AgentWorkspaceTerminalStartResult;
120
+ startTerminal(request: AgentWorkspaceTerminalStartRequest): Promise<AgentWorkspaceTerminalStartResult>;
106
121
  stopTerminal(request: AgentWorkspaceTerminalControlRequest): AgentWorkspaceTerminalControlResult;
107
122
  startPty(request: AgentWorkspacePtyStartRequest): Promise<AgentWorkspacePtyStartResult>;
108
123
  snapshotPty(request: AgentWorkspacePtySnapshotRequest): AgentWorkspacePtySnapshotResult;
@@ -111,6 +126,16 @@ export declare class WorkspaceHostService {
111
126
  clearPty(request: AgentWorkspaceTerminalControlRequest): AgentWorkspaceTerminalControlResult;
112
127
  stopPty(request: AgentWorkspaceTerminalControlRequest): AgentWorkspaceTerminalControlResult;
113
128
  close(): void;
129
+ /**
130
+ * The stream for a confined run — or a refusal.
131
+ *
132
+ * 🔴 When the run reports `sandboxed` there is no host fallback. A missing
133
+ * stream here is a composition bug; spawning `node-pty` as the signed-in
134
+ * user would be the lie `isolationIsNotObservation` exists to refuse.
135
+ */
136
+ private confineStream;
137
+ private startSandboxedTerminal;
138
+ private startSandboxedPty;
114
139
  private authorizePath;
115
140
  private authorizeSession;
116
141
  private effectiveAllowedRoots;
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceHostService.d.ts","sourceRoot":"","sources":["../../src/workspace/WorkspaceHostService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WorkspaceHostService.d.ts","sourceRoot":"","sources":["../../src/workspace/WorkspaceHostService.ts"],"names":[],"mappings":"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"}
@@ -1,21 +1,19 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { commandSucceeded } from '../isolation/CommandExecution.js';
3
+ import { Utf8StreamDecoder } from '../isolation/CommandStream.js';
3
4
  import { LocalCommandExecutor } from '../isolation/LocalCommandExecutor.js';
5
+ import { SANDBOX_STREAM_MAX_TIMEOUT_MS } from '../isolation/SandboxCommandStream.js';
6
+ import { commandShellArgv } from '../isolation/shellArgv.js';
4
7
  /**
5
- * ⚠️ This file has THREE process starts. Only ONE is adopted, and naming all
6
- * three matters an earlier version of this note said "the PTY is not part of
7
- * this adoption", which was true and skipped `startTerminal` entirely. Naming
8
- * one of several paths is the same mistake as naming the wrong one.
8
+ * ⚠️ This file has THREE process starts. Two of them are adopted when a run is
9
+ * sandboxed, and naming all three still matters.
9
10
  *
10
11
  * 1. `runCommand` — ADOPTED. Runs through `CommandExecutionPort`.
11
- * 2. `startTerminal` — still spawns directly. A fire-and-forget session that
12
- * outlives its request and is reaped by its own timer, so it is not the
13
- * port's one-call-one-result shape either.
14
- * 3. `startPty` a streaming, interactive session: stdin, resize, continuous
15
- * output. `xeno-use`'s exec surface has the SAME gap — `Command::output()`
16
- * buffers to completion — so streaming exec is recorded there as a new
17
- * capability rather than a defect. Forcing either through this port would
18
- * mean inventing a shape neither side has.
12
+ * 2. `startTerminal` — ADOPTED when confined (`CommandStreamPort` / exec-stream).
13
+ * Unconfined still spawns on the host.
14
+ * 3. `startPty` — ADOPTED when confined, through `CommandStreamPort`. The
15
+ * session's `tty` is asked from the runtime (`supports_exec_tty`), never
16
+ * assumed. Unconfined still uses `node-pty`.
19
17
  */
20
18
  const commandExecutor = new LocalCommandExecutor();
21
19
  import { closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync, } from 'node:fs';
@@ -300,20 +298,19 @@ export class WorkspaceHostService {
300
298
  * shell it runs in does not confine anything; these reduce what gets asked
301
299
  * for, and the substrate is what will refuse.
302
300
  *
303
- * `shellInvocation` already returns `{command, args}`, so this site needed
304
- * no argv change at all — it was already spelling a shell AS argv, which is
305
- * the pattern the port's rule generalises rather than imposes.
301
+ * argv comes from `commandShellArgv` so a sandboxed run does not send
302
+ * `powershell.exe` into a Linux container.
306
303
  */
307
- const invocation = shellInvocation(command, true);
308
304
  // 🔴 Resolved PER CALL, from the conversation this request belongs to.
309
305
  // Caching an executor on the service would outlive the run that justified
310
306
  // it and keep sending commands to a torn-down container — or, worse, keep
311
307
  // reporting containment after it ended. With no environment injected this
312
308
  // is the module's local executor, exactly as before.
313
- const executor = this.executionEnvironment?.executorFor(request.conversationId)
314
- ?? commandExecutor;
309
+ const resolution = this.executionEnvironment?.resolutionForConversation(request.conversationId);
310
+ const executor = resolution?.executor ?? commandExecutor;
311
+ const destination = resolution?.isolation.isolation === 'sandboxed' ? 'sandbox' : 'host';
315
312
  const result = await executor.execute({
316
- argv: [invocation.command, ...invocation.args],
313
+ argv: commandShellArgv(command, destination),
317
314
  cwd,
318
315
  timeoutMs,
319
316
  maxOutputBytes: MAX_COMMAND_OUTPUT_BYTES,
@@ -332,7 +329,7 @@ export class WorkspaceHostService {
332
329
  truncated: result.truncated,
333
330
  };
334
331
  }
335
- startTerminal(request) {
332
+ async startTerminal(request) {
336
333
  const command = String(request.command || '').trim();
337
334
  if (!command)
338
335
  return terminalStartFailure('Command is empty.');
@@ -344,8 +341,21 @@ export class WorkspaceHostService {
344
341
  const cwd = denied.path;
345
342
  const timeoutMs = clampInteger(request.timeoutMs, 300_000, 1_000, 900_000);
346
343
  const terminalId = uniqueId('terminal');
347
- const invocation = shellInvocation(command, false);
344
+ const confined = this.confineStream(request.conversationId);
345
+ if (confined.error)
346
+ return terminalStartFailure(confined.error);
347
+ if (confined.stream) {
348
+ return this.startSandboxedTerminal({
349
+ request,
350
+ command,
351
+ cwd,
352
+ terminalId,
353
+ timeoutMs,
354
+ stream: confined.stream,
355
+ });
356
+ }
348
357
  try {
358
+ const invocation = shellInvocation(command, false);
349
359
  const child = spawn(invocation.command, invocation.args, {
350
360
  cwd,
351
361
  windowsHide: true,
@@ -357,7 +367,7 @@ export class WorkspaceHostService {
357
367
  ownerInstanceId: request.client.instanceId,
358
368
  command,
359
369
  cwd,
360
- process: child,
370
+ process: { kill: () => { child.kill(); } },
361
371
  timer: null,
362
372
  timedOut: false,
363
373
  };
@@ -425,6 +435,19 @@ export class WorkspaceHostService {
425
435
  const terminalId = uniqueId('pty');
426
436
  const cols = clampInteger(request.cols, 80, 20, 240);
427
437
  const rows = clampInteger(request.rows, 24, 6, 120);
438
+ const confined = this.confineStream(request.conversationId);
439
+ if (confined.error)
440
+ return ptyStartFailure(confined.error, cwd);
441
+ if (confined.stream) {
442
+ return this.startSandboxedPty({
443
+ request,
444
+ cwd,
445
+ terminalId,
446
+ cols,
447
+ rows,
448
+ stream: confined.stream,
449
+ });
450
+ }
428
451
  try {
429
452
  const nodePty = await this.loadNodePty();
430
453
  if (!nodePty) {
@@ -444,7 +467,15 @@ export class WorkspaceHostService {
444
467
  id: terminalId,
445
468
  ownerInstanceId: request.client.instanceId,
446
469
  cwd,
447
- process: processHandle,
470
+ process: {
471
+ tty: true,
472
+ write: (data) => { processHandle.write(data); },
473
+ resize: (nextCols, nextRows) => {
474
+ processHandle.resize(nextCols, nextRows);
475
+ return { ok: true };
476
+ },
477
+ kill: () => { processHandle.kill(); },
478
+ },
448
479
  output: new ScrollbackBuffer(MAX_PTY_SCROLLBACK_BYTES),
449
480
  };
450
481
  this.ptySessions.set(terminalId, session);
@@ -465,7 +496,7 @@ export class WorkspaceHostService {
465
496
  ownerInstanceId: active?.ownerInstanceId || request.client.instanceId,
466
497
  });
467
498
  });
468
- return { success: true, terminalId, cwd, error: null };
499
+ return { success: true, terminalId, cwd, error: null, tty: true };
469
500
  }
470
501
  catch (error) {
471
502
  return { success: false, terminalId: null, cwd, error: safeError(error, 'Failed to start PTY terminal.') };
@@ -493,7 +524,9 @@ export class WorkspaceHostService {
493
524
  const denied = this.authorizeSession(request, session);
494
525
  if (denied)
495
526
  return { success: false, error: denied };
496
- session.process.resize(clampInteger(request.cols, 80, 20, 240), clampInteger(request.rows, 24, 6, 120));
527
+ const resized = session.process.resize(clampInteger(request.cols, 80, 20, 240), clampInteger(request.rows, 24, 6, 120));
528
+ if (!resized.ok)
529
+ return { success: false, error: resized.error };
497
530
  return { success: true, error: null };
498
531
  }
499
532
  clearPty(request) {
@@ -531,6 +564,155 @@ export class WorkspaceHostService {
531
564
  }
532
565
  this.ptySessions.clear();
533
566
  }
567
+ /**
568
+ * The stream for a confined run — or a refusal.
569
+ *
570
+ * 🔴 When the run reports `sandboxed` there is no host fallback. A missing
571
+ * stream here is a composition bug; spawning `node-pty` as the signed-in
572
+ * user would be the lie `isolationIsNotObservation` exists to refuse.
573
+ */
574
+ confineStream(conversationId) {
575
+ const resolution = this.executionEnvironment?.resolutionForConversation(conversationId);
576
+ if (resolution?.isolation.isolation !== 'sandboxed')
577
+ return {};
578
+ if (!resolution.stream) {
579
+ return { error: 'This run is sandboxed but has no command stream, so the terminal was not started.' };
580
+ }
581
+ return { stream: resolution.stream };
582
+ }
583
+ async startSandboxedTerminal(input) {
584
+ const decoder = new Utf8StreamDecoder();
585
+ let live;
586
+ this.terminalSessions.set(input.terminalId, {
587
+ id: input.terminalId,
588
+ ownerInstanceId: input.request.client.instanceId,
589
+ command: input.command,
590
+ cwd: input.cwd,
591
+ process: { kill: () => { void live?.kill(); } },
592
+ timer: null,
593
+ timedOut: false,
594
+ });
595
+ const started = await input.stream.start({
596
+ argv: commandShellArgv(input.command, 'sandbox'),
597
+ cwd: input.cwd,
598
+ timeoutMs: input.timeoutMs,
599
+ }, {
600
+ onChunk: (chunk) => {
601
+ const text = decoder.push(chunk.bytes);
602
+ if (!text)
603
+ return;
604
+ this.eventSink('workspace.terminal.data', {
605
+ terminalId: input.terminalId,
606
+ stream: chunk.stream,
607
+ chunk: text,
608
+ });
609
+ },
610
+ onExit: (exit) => {
611
+ const flushed = decoder.flush();
612
+ if (flushed) {
613
+ this.eventSink('workspace.terminal.data', {
614
+ terminalId: input.terminalId,
615
+ stream: 'stdout',
616
+ chunk: flushed,
617
+ });
618
+ }
619
+ const active = this.cleanupTerminal(input.terminalId);
620
+ if (!active)
621
+ return;
622
+ this.eventSink('workspace.terminal.exit', {
623
+ terminalId: input.terminalId,
624
+ command: input.command,
625
+ cwd: input.cwd,
626
+ success: !active.timedOut && exit.exitCode === 0,
627
+ exitCode: exit.exitCode,
628
+ error: active.timedOut || exit.timedOut
629
+ ? `Command timed out after ${input.timeoutMs}ms.`
630
+ : null,
631
+ ownerInstanceId: active.ownerInstanceId,
632
+ });
633
+ },
634
+ });
635
+ if (started.error || !started.session) {
636
+ this.terminalSessions.delete(input.terminalId);
637
+ return terminalStartFailure(started.error || 'Failed to start sandboxed terminal command.');
638
+ }
639
+ live = started.session;
640
+ return { success: true, terminalId: input.terminalId, cwd: input.cwd, error: null };
641
+ }
642
+ async startSandboxedPty(input) {
643
+ const decoder = new Utf8StreamDecoder();
644
+ const output = new ScrollbackBuffer(MAX_PTY_SCROLLBACK_BYTES);
645
+ let live;
646
+ this.ptySessions.set(input.terminalId, {
647
+ id: input.terminalId,
648
+ ownerInstanceId: input.request.client.instanceId,
649
+ cwd: input.cwd,
650
+ process: sandboxPtyHandle({
651
+ get tty() { return live?.tty === true; },
652
+ write: (bytes) => live ? live.write(bytes) : Promise.resolve(),
653
+ closeStdin: () => live ? live.closeStdin() : Promise.resolve(),
654
+ resize: (cols, rows) => live
655
+ ? live.resize(cols, rows)
656
+ : Promise.resolve({ ok: false, error: 'the sandbox PTY has not started' }),
657
+ kill: () => live ? live.kill() : Promise.resolve(),
658
+ }),
659
+ output,
660
+ });
661
+ const started = await input.stream.start({
662
+ argv: ['/bin/sh', '-i'],
663
+ cwd: input.cwd,
664
+ timeoutMs: SANDBOX_STREAM_MAX_TIMEOUT_MS,
665
+ tty: true,
666
+ }, {
667
+ onChunk: (chunk) => {
668
+ const text = decoder.push(chunk.bytes);
669
+ if (!text)
670
+ return;
671
+ const active = this.ptySessions.get(input.terminalId);
672
+ if (!active)
673
+ return;
674
+ active.output.append(text);
675
+ this.eventSink('workspace.pty.data', {
676
+ terminalId: input.terminalId,
677
+ data: text,
678
+ ownerInstanceId: active.ownerInstanceId,
679
+ });
680
+ },
681
+ onExit: (exit) => {
682
+ const flushed = decoder.flush();
683
+ const active = this.ptySessions.get(input.terminalId);
684
+ if (flushed && active)
685
+ active.output.append(flushed);
686
+ this.ptySessions.delete(input.terminalId);
687
+ this.eventSink('workspace.pty.exit', {
688
+ terminalId: input.terminalId,
689
+ exitCode: exit.exitCode,
690
+ signal: null,
691
+ ownerInstanceId: active?.ownerInstanceId || input.request.client.instanceId,
692
+ });
693
+ },
694
+ });
695
+ if (started.error || !started.session) {
696
+ this.ptySessions.delete(input.terminalId);
697
+ return ptyStartFailure(started.error || 'Failed to start sandboxed PTY.', input.cwd);
698
+ }
699
+ live = started.session;
700
+ if (started.session.tty) {
701
+ const sized = await started.session.resize(input.cols, input.rows);
702
+ if (!sized.ok) {
703
+ await started.session.kill();
704
+ this.ptySessions.delete(input.terminalId);
705
+ return ptyStartFailure(sized.error, input.cwd);
706
+ }
707
+ }
708
+ return {
709
+ success: true,
710
+ terminalId: input.terminalId,
711
+ cwd: input.cwd,
712
+ error: null,
713
+ tty: started.session.tty,
714
+ };
715
+ }
534
716
  authorizePath(request, rawPath, action, id, allowMissing = false) {
535
717
  const path = cleanPath(rawPath);
536
718
  if (!path)
@@ -835,6 +1017,20 @@ function terminalStartFailure(error) {
835
1017
  function ptyStartFailure(error, cwd = null) {
836
1018
  return { success: false, terminalId: null, cwd, error };
837
1019
  }
1020
+ function sandboxPtyHandle(session) {
1021
+ return {
1022
+ get tty() { return session.tty; },
1023
+ write: (data) => { void session.write(Buffer.from(data, 'utf8')); },
1024
+ resize: (cols, rows) => {
1025
+ if (!session.tty) {
1026
+ return { ok: false, error: 'this runtime has no sandbox TTY (supports_exec_tty is false)' };
1027
+ }
1028
+ void session.resize(cols, rows);
1029
+ return { ok: true };
1030
+ },
1031
+ kill: () => { void session.kill(); },
1032
+ };
1033
+ }
838
1034
  function safeError(error, fallback) {
839
1035
  return error instanceof Error && error.message.trim() ? error.message.slice(0, 1_000) : fallback;
840
1036
  }