@tangle-network/agent-app 0.44.57 → 0.44.59

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 '../use-file-mentions-CZ-Ua_sb.js';
11
11
  import '../agent-activity-C8ZG0F0M.js';
12
12
  import '../flow-types-CJxEmaRy.js';
13
13
  import '../queue-VTBA5ONX.js';
14
- import '../sandbox-terminal-ChNEdHF8.js';
14
+ import '../web-react/terminal.js';
15
15
  import '../billing-BibxgALe.js';
16
16
  import '../billing/index.js';
17
17
  import '../session-shell/index.js';
@@ -11,4 +11,4 @@ export {
11
11
  DesignCanvasLazy,
12
12
  DesignCanvasChromeLazy
13
13
  };
14
- //# sourceMappingURL=chunk-OO4DROKM.js.map
14
+ //# sourceMappingURL=chunk-DMYCGXBP.js.map
@@ -91,7 +91,7 @@ import {
91
91
  import {
92
92
  DesignCanvasChromeLazy,
93
93
  DesignCanvasLazy
94
- } from "../chunk-OO4DROKM.js";
94
+ } from "../chunk-DMYCGXBP.js";
95
95
  import "../chunk-J25RST7G.js";
96
96
 
97
97
  // src/design-canvas-react/components/CanvasInsertPanel.tsx
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DesignCanvasChromeLazy,
3
3
  DesignCanvasLazy
4
- } from "../chunk-OO4DROKM.js";
4
+ } from "../chunk-DMYCGXBP.js";
5
5
  export {
6
6
  DesignCanvasChromeLazy,
7
7
  DesignCanvasLazy
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Server-only policy sibling of the React `./vault` client surface. Pure
3
+ * decision logic — no I/O, no logging, ZERO imports — extracted from
4
+ * gtm-agent's local guard (gtm#612) after it lost 48 production vault files:
5
+ * a half-mounted sandbox scanned cleanly and reconciliation derived a
6
+ * mass-deletion batch from an empty manifest. The #265/#332 audit ruled both
7
+ * checks here universal shell mechanism, not gtm-specific, so they live here
8
+ * once instead of being re-forked per product.
9
+ *
10
+ * Two independent checks:
11
+ * - `assessVaultDeletionBatch` — blast-radius refusal. Given the live
12
+ * baseline and what a reconciliation pass proposes to delete, decide
13
+ * whether the batch is safe to apply or must be refused. Refusal is a
14
+ * SUCCESSFUL outcome, not a thrown error: content changes still apply,
15
+ * only the deletions are withheld. What to do with a refusal (log, alert,
16
+ * retry the scan) stays the caller's job.
17
+ * - `compareIncarnationBaseline` — filesystem-incarnation comparison. Given
18
+ * a recorded baseline incarnation id and the sandbox's current identity
19
+ * fields, decide whether the sandbox filesystem is the same one the
20
+ * baseline was captured against, fail-closed on anything not clearly
21
+ * identified and ready.
22
+ *
23
+ * Both are pure functions over caller-supplied data: no manifest reads, no
24
+ * sandbox calls, no console output. The caller pre-filters tombstoned
25
+ * entries (this module only ever sees the LIVE path set) and owns everything
26
+ * that happens after a verdict comes back.
27
+ */
28
+ /** Refuse a deletion batch once it would remove at least this fraction of the
29
+ * live baseline (both this and `VAULT_DELETION_REFUSAL_MIN_LIVE_FILES` must
30
+ * hold — a tiny vault can lose all its files without tripping this ratio
31
+ * gate; `refusesAllFiles` catches that case separately). */
32
+ declare const VAULT_DELETION_REFUSAL_RATIO = 0.75;
33
+ /** The blast-radius ratio gate only engages once the live baseline is at
34
+ * least this large, so a 2-file vault losing 1 file (50%) is not refused on
35
+ * ratio alone. */
36
+ declare const VAULT_DELETION_REFUSAL_MIN_LIVE_FILES = 10;
37
+ /** Overrides for `assessVaultDeletionBatch`'s two thresholds. Both default to
38
+ * the exported constants above. Values are validated fail-loud:
39
+ * `refusalRatio` must be finite and in (0, 1] — `NaN` (a config parse
40
+ * failure) would make `deletionRatio >= refusalRatio` always false and silently
41
+ * disable the guard, and `0` would refuse a zero-deletion batch; and
42
+ * `minLiveFiles` must be finite and >= 0. Invalid values throw `RangeError`
43
+ * instead of running with a guard that cannot fire (or cannot pass). */
44
+ interface VaultDeletionPolicy {
45
+ refusalRatio?: number;
46
+ minLiveFiles?: number;
47
+ }
48
+ interface VaultDeletionAssessment {
49
+ /** True when no refusal reason fired. */
50
+ allowed: boolean;
51
+ /** Precedence when multiple reasons would fire: `empty-manifest` beats
52
+ * `all-files` beats `ratio-exceeded` (most specific first — an empty scan
53
+ * IS an all-files deletion, but the diagnostic that matters is "the scan
54
+ * came back empty", not "it deleted everything"). Undefined when allowed. */
55
+ reason?: 'empty-manifest' | 'all-files' | 'ratio-exceeded';
56
+ /** wouldDelete / baselineLive, or 0 when the baseline is itself empty. */
57
+ deletionRatio: number;
58
+ wouldDelete: number;
59
+ /** The paths that would be deleted, sorted — mirrors gtm's `.sort()` so log
60
+ * output and any snapshot fixture are byte-stable. */
61
+ wouldDeletePaths: string[];
62
+ baselineLive: number;
63
+ }
64
+ /**
65
+ * Decide whether a reconciliation pass's proposed deletions are safe to
66
+ * apply against the live baseline.
67
+ *
68
+ * `manifestEmpty: true` means the filesystem scan that produced
69
+ * `proposedDeletions` came back with nothing — the caller could not have
70
+ * derived a meaningful proposed-deletions list, so this treats the FULL live
71
+ * baseline as would-delete regardless of what `proposedDeletions` contains.
72
+ * That is what distinguishes `'empty-manifest'` from `'all-files'`: an empty
73
+ * scan structurally implies every baseline path would be deleted, but the
74
+ * diagnostic that matters to a caller is "the scan was empty", not merely
75
+ * "everything would go".
76
+ */
77
+ declare function assessVaultDeletionBatch(input: {
78
+ /** LIVE (non-tombstoned) baseline path set — the caller pre-filters, as gtm does. */
79
+ baselinePaths: readonly string[];
80
+ /** Paths a reconciliation pass proposes to delete; intersected with `baselinePaths` internally. */
81
+ proposedDeletions: readonly string[];
82
+ manifestEmpty?: boolean;
83
+ policy?: VaultDeletionPolicy;
84
+ }): VaultDeletionAssessment;
85
+ /** The subset of a sandbox's filesystem-incarnation identity this module
86
+ * needs. A product's real incarnation type is structurally compatible —
87
+ * no import required. */
88
+ interface FilesystemIncarnationLike {
89
+ filesystemIncarnationId?: string;
90
+ filesystemIncarnationProvenance?: 'fresh' | 'restored' | 'unknown';
91
+ filesystemIncarnationReadiness?: 'transitioning' | 'ready';
92
+ }
93
+ /**
94
+ * Result of comparing a recorded baseline incarnation id against the
95
+ * sandbox's current identity. A discriminated union over `verdict` so a
96
+ * caller's `switch` gets compile-time coverage instead of a stringly-typed
97
+ * status it can forget to branch on.
98
+ */
99
+ type IncarnationComparison = {
100
+ verdict: 'match';
101
+ } | {
102
+ verdict: 'mismatch';
103
+ baselineId: string;
104
+ currentId: string;
105
+ } | {
106
+ verdict: 'not-ready';
107
+ readiness: string | undefined;
108
+ } | {
109
+ verdict: 'unidentified';
110
+ } | {
111
+ verdict: 'no-baseline';
112
+ };
113
+ /**
114
+ * Compare a recorded baseline filesystem-incarnation id against a sandbox's
115
+ * current incarnation fields. Check order mirrors gtm's gate sequence and is
116
+ * itself a tested fail-closed property — each earlier check short-circuits
117
+ * the ones after it, so a box with BOTH bad readiness and a mismatched id
118
+ * resolves `'not-ready'`, never `'mismatch'`:
119
+ *
120
+ * 1. `unidentified` — the current id is missing/empty, or the provenance is
121
+ * not one of `fresh`/`restored`/`unknown`.
122
+ * 2. `not-ready` — readiness is anything other than the literal string
123
+ * `'ready'` (this also catches `undefined`).
124
+ * 3. `no-baseline` — no baseline id was ever recorded to compare against.
125
+ * 4. `mismatch` — the baseline id and current id disagree.
126
+ * 5. `match` — same incarnation.
127
+ */
128
+ declare function compareIncarnationBaseline(baselineId: string | undefined, current: FilesystemIncarnationLike): IncarnationComparison;
129
+
130
+ export { type FilesystemIncarnationLike, type IncarnationComparison, VAULT_DELETION_REFUSAL_MIN_LIVE_FILES, VAULT_DELETION_REFUSAL_RATIO, type VaultDeletionAssessment, type VaultDeletionPolicy, assessVaultDeletionBatch, compareIncarnationBaseline };
@@ -0,0 +1,60 @@
1
+ // src/vault/server.ts
2
+ var VAULT_DELETION_REFUSAL_RATIO = 0.75;
3
+ var VAULT_DELETION_REFUSAL_MIN_LIVE_FILES = 10;
4
+ function assessVaultDeletionBatch(input) {
5
+ const refusalRatio = input.policy?.refusalRatio ?? VAULT_DELETION_REFUSAL_RATIO;
6
+ const minLiveFiles = input.policy?.minLiveFiles ?? VAULT_DELETION_REFUSAL_MIN_LIVE_FILES;
7
+ if (!Number.isFinite(refusalRatio) || refusalRatio <= 0 || refusalRatio > 1) {
8
+ throw new RangeError(
9
+ `vault deletion policy refusalRatio must be a finite number in (0, 1], got ${refusalRatio}`
10
+ );
11
+ }
12
+ if (!Number.isFinite(minLiveFiles) || minLiveFiles < 0) {
13
+ throw new RangeError(
14
+ `vault deletion policy minLiveFiles must be a finite number >= 0, got ${minLiveFiles}`
15
+ );
16
+ }
17
+ const manifestEmpty = input.manifestEmpty ?? false;
18
+ const baselineLive = input.baselinePaths.length;
19
+ const proposedSet = new Set(input.proposedDeletions);
20
+ const wouldDeletePaths = manifestEmpty ? [...input.baselinePaths].sort() : input.baselinePaths.filter((p) => proposedSet.has(p)).sort();
21
+ const wouldDelete = wouldDeletePaths.length;
22
+ const deletionRatio = baselineLive > 0 ? wouldDelete / baselineLive : 0;
23
+ const refusesEmptyManifest = manifestEmpty && baselineLive > 0;
24
+ const refusesAllFiles = wouldDelete > 0 && wouldDelete === baselineLive;
25
+ const refusesBlastRadius = wouldDelete > 0 && baselineLive >= minLiveFiles && deletionRatio >= refusalRatio;
26
+ const reason = refusesEmptyManifest ? "empty-manifest" : refusesAllFiles ? "all-files" : refusesBlastRadius ? "ratio-exceeded" : void 0;
27
+ return {
28
+ allowed: reason === void 0,
29
+ reason,
30
+ deletionRatio,
31
+ wouldDelete,
32
+ wouldDeletePaths,
33
+ baselineLive
34
+ };
35
+ }
36
+ function compareIncarnationBaseline(baselineId, current) {
37
+ const currentId = current.filesystemIncarnationId;
38
+ const provenance = current.filesystemIncarnationProvenance;
39
+ const isValidProvenance = provenance === "fresh" || provenance === "restored" || provenance === "unknown";
40
+ if (typeof currentId !== "string" || currentId.length === 0 || !isValidProvenance) {
41
+ return { verdict: "unidentified" };
42
+ }
43
+ if (current.filesystemIncarnationReadiness !== "ready") {
44
+ return { verdict: "not-ready", readiness: current.filesystemIncarnationReadiness };
45
+ }
46
+ if (baselineId === void 0) {
47
+ return { verdict: "no-baseline" };
48
+ }
49
+ if (baselineId !== currentId) {
50
+ return { verdict: "mismatch", baselineId, currentId };
51
+ }
52
+ return { verdict: "match" };
53
+ }
54
+ export {
55
+ VAULT_DELETION_REFUSAL_MIN_LIVE_FILES,
56
+ VAULT_DELETION_REFUSAL_RATIO,
57
+ assessVaultDeletionBatch,
58
+ compareIncarnationBaseline
59
+ };
60
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vault/server.ts"],"sourcesContent":["/**\n * Server-only policy sibling of the React `./vault` client surface. Pure\n * decision logic — no I/O, no logging, ZERO imports — extracted from\n * gtm-agent's local guard (gtm#612) after it lost 48 production vault files:\n * a half-mounted sandbox scanned cleanly and reconciliation derived a\n * mass-deletion batch from an empty manifest. The #265/#332 audit ruled both\n * checks here universal shell mechanism, not gtm-specific, so they live here\n * once instead of being re-forked per product.\n *\n * Two independent checks:\n * - `assessVaultDeletionBatch` — blast-radius refusal. Given the live\n * baseline and what a reconciliation pass proposes to delete, decide\n * whether the batch is safe to apply or must be refused. Refusal is a\n * SUCCESSFUL outcome, not a thrown error: content changes still apply,\n * only the deletions are withheld. What to do with a refusal (log, alert,\n * retry the scan) stays the caller's job.\n * - `compareIncarnationBaseline` — filesystem-incarnation comparison. Given\n * a recorded baseline incarnation id and the sandbox's current identity\n * fields, decide whether the sandbox filesystem is the same one the\n * baseline was captured against, fail-closed on anything not clearly\n * identified and ready.\n *\n * Both are pure functions over caller-supplied data: no manifest reads, no\n * sandbox calls, no console output. The caller pre-filters tombstoned\n * entries (this module only ever sees the LIVE path set) and owns everything\n * that happens after a verdict comes back.\n */\n\n/** Refuse a deletion batch once it would remove at least this fraction of the\n * live baseline (both this and `VAULT_DELETION_REFUSAL_MIN_LIVE_FILES` must\n * hold — a tiny vault can lose all its files without tripping this ratio\n * gate; `refusesAllFiles` catches that case separately). */\nexport const VAULT_DELETION_REFUSAL_RATIO = 0.75\n\n/** The blast-radius ratio gate only engages once the live baseline is at\n * least this large, so a 2-file vault losing 1 file (50%) is not refused on\n * ratio alone. */\nexport const VAULT_DELETION_REFUSAL_MIN_LIVE_FILES = 10\n\n/** Overrides for `assessVaultDeletionBatch`'s two thresholds. Both default to\n * the exported constants above. Values are validated fail-loud:\n * `refusalRatio` must be finite and in (0, 1] — `NaN` (a config parse\n * failure) would make `deletionRatio >= refusalRatio` always false and silently\n * disable the guard, and `0` would refuse a zero-deletion batch; and\n * `minLiveFiles` must be finite and >= 0. Invalid values throw `RangeError`\n * instead of running with a guard that cannot fire (or cannot pass). */\nexport interface VaultDeletionPolicy {\n refusalRatio?: number\n minLiveFiles?: number\n}\n\nexport interface VaultDeletionAssessment {\n /** True when no refusal reason fired. */\n allowed: boolean\n /** Precedence when multiple reasons would fire: `empty-manifest` beats\n * `all-files` beats `ratio-exceeded` (most specific first — an empty scan\n * IS an all-files deletion, but the diagnostic that matters is \"the scan\n * came back empty\", not \"it deleted everything\"). Undefined when allowed. */\n reason?: 'empty-manifest' | 'all-files' | 'ratio-exceeded'\n /** wouldDelete / baselineLive, or 0 when the baseline is itself empty. */\n deletionRatio: number\n wouldDelete: number\n /** The paths that would be deleted, sorted — mirrors gtm's `.sort()` so log\n * output and any snapshot fixture are byte-stable. */\n wouldDeletePaths: string[]\n baselineLive: number\n}\n\n/**\n * Decide whether a reconciliation pass's proposed deletions are safe to\n * apply against the live baseline.\n *\n * `manifestEmpty: true` means the filesystem scan that produced\n * `proposedDeletions` came back with nothing — the caller could not have\n * derived a meaningful proposed-deletions list, so this treats the FULL live\n * baseline as would-delete regardless of what `proposedDeletions` contains.\n * That is what distinguishes `'empty-manifest'` from `'all-files'`: an empty\n * scan structurally implies every baseline path would be deleted, but the\n * diagnostic that matters to a caller is \"the scan was empty\", not merely\n * \"everything would go\".\n */\nexport function assessVaultDeletionBatch(input: {\n /** LIVE (non-tombstoned) baseline path set — the caller pre-filters, as gtm does. */\n baselinePaths: readonly string[]\n /** Paths a reconciliation pass proposes to delete; intersected with `baselinePaths` internally. */\n proposedDeletions: readonly string[]\n manifestEmpty?: boolean\n policy?: VaultDeletionPolicy\n}): VaultDeletionAssessment {\n const refusalRatio = input.policy?.refusalRatio ?? VAULT_DELETION_REFUSAL_RATIO\n const minLiveFiles = input.policy?.minLiveFiles ?? VAULT_DELETION_REFUSAL_MIN_LIVE_FILES\n if (!Number.isFinite(refusalRatio) || refusalRatio <= 0 || refusalRatio > 1) {\n throw new RangeError(\n `vault deletion policy refusalRatio must be a finite number in (0, 1], got ${refusalRatio}`,\n )\n }\n if (!Number.isFinite(minLiveFiles) || minLiveFiles < 0) {\n throw new RangeError(\n `vault deletion policy minLiveFiles must be a finite number >= 0, got ${minLiveFiles}`,\n )\n }\n const manifestEmpty = input.manifestEmpty ?? false\n\n const baselineLive = input.baselinePaths.length\n const proposedSet = new Set(input.proposedDeletions)\n const wouldDeletePaths = manifestEmpty\n ? [...input.baselinePaths].sort()\n : input.baselinePaths.filter((p) => proposedSet.has(p)).sort()\n const wouldDelete = wouldDeletePaths.length\n const deletionRatio = baselineLive > 0 ? wouldDelete / baselineLive : 0\n\n const refusesEmptyManifest = manifestEmpty && baselineLive > 0\n const refusesAllFiles = wouldDelete > 0 && wouldDelete === baselineLive\n // `wouldDelete > 0` is unreachable-false under the validated policy domain\n // (a positive refusalRatio can never be met by a 0 ratio) — it is here so\n // the ratio branch stays safe on its own terms, independent of the\n // validation above ever being relaxed.\n const refusesBlastRadius = wouldDelete > 0\n && baselineLive >= minLiveFiles\n && deletionRatio >= refusalRatio\n\n const reason = refusesEmptyManifest\n ? ('empty-manifest' as const)\n : refusesAllFiles\n ? ('all-files' as const)\n : refusesBlastRadius\n ? ('ratio-exceeded' as const)\n : undefined\n\n return {\n allowed: reason === undefined,\n reason,\n deletionRatio,\n wouldDelete,\n wouldDeletePaths,\n baselineLive,\n }\n}\n\n/** The subset of a sandbox's filesystem-incarnation identity this module\n * needs. A product's real incarnation type is structurally compatible —\n * no import required. */\nexport interface FilesystemIncarnationLike {\n filesystemIncarnationId?: string\n filesystemIncarnationProvenance?: 'fresh' | 'restored' | 'unknown'\n filesystemIncarnationReadiness?: 'transitioning' | 'ready'\n}\n\n/**\n * Result of comparing a recorded baseline incarnation id against the\n * sandbox's current identity. A discriminated union over `verdict` so a\n * caller's `switch` gets compile-time coverage instead of a stringly-typed\n * status it can forget to branch on.\n */\nexport type IncarnationComparison =\n | { verdict: 'match' }\n | { verdict: 'mismatch'; baselineId: string; currentId: string }\n | { verdict: 'not-ready'; readiness: string | undefined }\n | { verdict: 'unidentified' }\n | { verdict: 'no-baseline' }\n\n/**\n * Compare a recorded baseline filesystem-incarnation id against a sandbox's\n * current incarnation fields. Check order mirrors gtm's gate sequence and is\n * itself a tested fail-closed property — each earlier check short-circuits\n * the ones after it, so a box with BOTH bad readiness and a mismatched id\n * resolves `'not-ready'`, never `'mismatch'`:\n *\n * 1. `unidentified` — the current id is missing/empty, or the provenance is\n * not one of `fresh`/`restored`/`unknown`.\n * 2. `not-ready` — readiness is anything other than the literal string\n * `'ready'` (this also catches `undefined`).\n * 3. `no-baseline` — no baseline id was ever recorded to compare against.\n * 4. `mismatch` — the baseline id and current id disagree.\n * 5. `match` — same incarnation.\n */\nexport function compareIncarnationBaseline(\n baselineId: string | undefined,\n current: FilesystemIncarnationLike,\n): IncarnationComparison {\n const currentId = current.filesystemIncarnationId\n const provenance = current.filesystemIncarnationProvenance\n const isValidProvenance = provenance === 'fresh' || provenance === 'restored' || provenance === 'unknown'\n\n if (typeof currentId !== 'string' || currentId.length === 0 || !isValidProvenance) {\n return { verdict: 'unidentified' }\n }\n\n if (current.filesystemIncarnationReadiness !== 'ready') {\n return { verdict: 'not-ready', readiness: current.filesystemIncarnationReadiness }\n }\n\n if (baselineId === undefined) {\n return { verdict: 'no-baseline' }\n }\n\n if (baselineId !== currentId) {\n return { verdict: 'mismatch', baselineId, currentId }\n }\n\n return { verdict: 'match' }\n}\n"],"mappings":";AAgCO,IAAM,+BAA+B;AAKrC,IAAM,wCAAwC;AA4C9C,SAAS,yBAAyB,OAOb;AAC1B,QAAM,eAAe,MAAM,QAAQ,gBAAgB;AACnD,QAAM,eAAe,MAAM,QAAQ,gBAAgB;AACnD,MAAI,CAAC,OAAO,SAAS,YAAY,KAAK,gBAAgB,KAAK,eAAe,GAAG;AAC3E,UAAM,IAAI;AAAA,MACR,6EAA6E,YAAY;AAAA,IAC3F;AAAA,EACF;AACA,MAAI,CAAC,OAAO,SAAS,YAAY,KAAK,eAAe,GAAG;AACtD,UAAM,IAAI;AAAA,MACR,wEAAwE,YAAY;AAAA,IACtF;AAAA,EACF;AACA,QAAM,gBAAgB,MAAM,iBAAiB;AAE7C,QAAM,eAAe,MAAM,cAAc;AACzC,QAAM,cAAc,IAAI,IAAI,MAAM,iBAAiB;AACnD,QAAM,mBAAmB,gBACrB,CAAC,GAAG,MAAM,aAAa,EAAE,KAAK,IAC9B,MAAM,cAAc,OAAO,CAAC,MAAM,YAAY,IAAI,CAAC,CAAC,EAAE,KAAK;AAC/D,QAAM,cAAc,iBAAiB;AACrC,QAAM,gBAAgB,eAAe,IAAI,cAAc,eAAe;AAEtE,QAAM,uBAAuB,iBAAiB,eAAe;AAC7D,QAAM,kBAAkB,cAAc,KAAK,gBAAgB;AAK3D,QAAM,qBAAqB,cAAc,KACpC,gBAAgB,gBAChB,iBAAiB;AAEtB,QAAM,SAAS,uBACV,mBACD,kBACG,cACD,qBACG,mBACD;AAER,SAAO;AAAA,IACL,SAAS,WAAW;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAuCO,SAAS,2BACd,YACA,SACuB;AACvB,QAAM,YAAY,QAAQ;AAC1B,QAAM,aAAa,QAAQ;AAC3B,QAAM,oBAAoB,eAAe,WAAW,eAAe,cAAc,eAAe;AAEhG,MAAI,OAAO,cAAc,YAAY,UAAU,WAAW,KAAK,CAAC,mBAAmB;AACjF,WAAO,EAAE,SAAS,eAAe;AAAA,EACnC;AAEA,MAAI,QAAQ,mCAAmC,SAAS;AACtD,WAAO,EAAE,SAAS,aAAa,WAAW,QAAQ,+BAA+B;AAAA,EACnF;AAEA,MAAI,eAAe,QAAW;AAC5B,WAAO,EAAE,SAAS,cAAc;AAAA,EAClC;AAEA,MAAI,eAAe,WAAW;AAC5B,WAAO,EAAE,SAAS,YAAY,YAAY,UAAU;AAAA,EACtD;AAEA,SAAO,EAAE,SAAS,QAAQ;AAC5B;","names":[]}
@@ -15,7 +15,7 @@ import { S as StepAgentActivity } from '../agent-activity-C8ZG0F0M.js';
15
15
  import { F as FlowTrace } from '../flow-types-CJxEmaRy.js';
16
16
  import { a as ReviewQueueItem, c as ReviewQueueState } from '../queue-VTBA5ONX.js';
17
17
  export { p as parseReviewQueueItem } from '../queue-VTBA5ONX.js';
18
- export { S as SandboxTerminalConnection, a as SandboxTerminalConnectionResponse, U as UseSandboxTerminalConnectionOptions, b as UseSandboxTerminalConnectionResult, t as tabTerminalConnectionId, u as useSandboxTerminalConnection } from '../sandbox-terminal-ChNEdHF8.js';
18
+ export { SandboxTerminalConnection, SandboxTerminalConnectionResponse, UseSandboxTerminalConnectionOptions, UseSandboxTerminalConnectionResult, tabTerminalConnectionId, useSandboxTerminalConnection } from './terminal.js';
19
19
  import { i as ProductSeatOffer } from '../billing-BibxgALe.js';
20
20
  import { SessionSort, SessionPage, SessionSummary, SessionRailAction } from '../session-shell/index.js';
21
21
  import { CatalogModel } from '../catalog/index.js';
@@ -1,49 +1,51 @@
1
- import { S as SandboxTerminalConnection } from '../sandbox-terminal-ChNEdHF8.js';
2
- export { a as SandboxTerminalConnectionResponse, U as UseSandboxTerminalConnectionOptions, b as UseSandboxTerminalConnectionResult, t as tabTerminalConnectionId, u as useSandboxTerminalConnection } from '../sandbox-terminal-ChNEdHF8.js';
3
- import { ReactNode } from 'react';
4
-
1
+ /** Define the connection details and status for a sandbox terminal session */
2
+ interface SandboxTerminalConnection {
3
+ runtimeUrl: string | null;
4
+ sidecarUrl: string | null;
5
+ token: string | null;
6
+ expiresAt: string | null;
7
+ status: string;
8
+ error: string | null;
9
+ loading: boolean;
10
+ sandboxId?: string;
11
+ }
12
+ /** Define the response structure for a sandbox terminal connection including URLs, token, status, and errors */
13
+ interface SandboxTerminalConnectionResponse {
14
+ runtimeUrl?: string;
15
+ sidecarUrl?: string;
16
+ token?: string;
17
+ expiresAt?: string;
18
+ status?: string;
19
+ error?: string;
20
+ sandboxId?: string;
21
+ }
22
+ /** Define options for configuring a sandbox terminal connection including workspace ID and connection parameters */
23
+ interface UseSandboxTerminalConnectionOptions {
24
+ workspaceId: string;
25
+ connectionUrl?: string | ((workspaceId: string) => string);
26
+ fetcher?: typeof fetch;
27
+ provisionPollIntervalMs?: number;
28
+ provisionPollTimeoutMs?: number;
29
+ tokenRefreshSkewMs?: number;
30
+ }
31
+ /** Resolve sandbox terminal connection status and provide a method to initiate the connection */
32
+ interface UseSandboxTerminalConnectionResult extends SandboxTerminalConnection {
33
+ connect: () => Promise<void>;
34
+ }
35
+ /** Manage and maintain a sandbox terminal connection with automatic polling and token refresh handling */
36
+ declare function useSandboxTerminalConnection(opts: UseSandboxTerminalConnectionOptions): UseSandboxTerminalConnectionResult;
5
37
  /**
6
- * `WorkspaceTerminalPanel` the shared sandbox-terminal surface: a header with
7
- * a status badge, connect/provisioning/error states with a retry, and the lazy
8
- * `TerminalView` (from `@tangle-network/sandbox-ui`) mounted only once the
9
- * connection is live. creative-agent and gtm-agent each hand-roll a structurally
10
- * identical panel; only the copy and the status-tone map are app-specific, so
11
- * those are props and everything else lives here.
38
+ * Stable-per-tab, unique-per-client terminal connection id.
12
39
  *
13
- * Pair it with {@link useSandboxTerminalConnection} (the `connection` prop) and
14
- * {@link tabTerminalConnectionId} (the `connectionId` prop) so reloads restore
15
- * the same PTY and separate tabs don't evict each other.
40
+ * Persists in `sessionStorage` so a reload in the same tab reuses the id (the
41
+ * sidecar restores the same PTY session via `TerminalView.connectionId`), while
42
+ * separate tabs/windows each get a distinct id. Pass the result as
43
+ * `TerminalView`'s `connectionId`. Without it (e.g. gtm-agent today) every tab
44
+ * shares one connection id and their reconnects evict each other.
16
45
  *
17
- * Styling matches the rest of `web-react`: Tailwind over the shared design
18
- * tokens; glyphs inline; no icon/UI library.
46
+ * Falls back to an ephemeral id when `sessionStorage` is unavailable (SSR,
47
+ * privacy mode) still unique per call, just not reload-stable.
19
48
  */
49
+ declare function tabTerminalConnectionId(storageKey?: string): string;
20
50
 
21
- type TerminalStatusTone = 'idle' | 'connecting' | 'connected' | 'error';
22
- interface TerminalStatusDisplay {
23
- tone: TerminalStatusTone;
24
- label: string;
25
- }
26
- interface WorkspaceTerminalPanelProps {
27
- /** Live connection state (from {@link useSandboxTerminalConnection}). */
28
- connection: SandboxTerminalConnection;
29
- /** Stable per-tab id (from {@link tabTerminalConnectionId}) so the sidecar
30
- * restores the same PTY across remounts and tabs don't collide. */
31
- connectionId?: string;
32
- /** Header title. Default "Terminal". */
33
- title?: string;
34
- /** Header subtitle / sandbox label. */
35
- subtitle?: string;
36
- /** Whether the terminal tab is visible (forwarded to `TerminalView` for fit). */
37
- isActive?: boolean;
38
- /** Reconnect handler — wire to the hook's `connect`. Shown on idle/error. */
39
- onRetry?: () => void;
40
- /** Map a `connection.status` to a badge tone + label. The default covers
41
- * idle/provisioning/running/error; override for app-specific vocabulary. */
42
- statusDisplay?: (connection: SandboxTerminalConnection) => TerminalStatusDisplay;
43
- /** Extra header content, right-aligned (actions, sandbox id, …). */
44
- headerExtra?: ReactNode;
45
- className?: string;
46
- }
47
- declare function WorkspaceTerminalPanel({ connection, connectionId, title, subtitle, isActive, onRetry, statusDisplay, headerExtra, className, }: WorkspaceTerminalPanelProps): ReactNode;
48
-
49
- export { SandboxTerminalConnection, type TerminalStatusDisplay, type TerminalStatusTone, WorkspaceTerminalPanel, type WorkspaceTerminalPanelProps };
51
+ export { type SandboxTerminalConnection, type SandboxTerminalConnectionResponse, type UseSandboxTerminalConnectionOptions, type UseSandboxTerminalConnectionResult, tabTerminalConnectionId, useSandboxTerminalConnection };
@@ -2,93 +2,7 @@ import {
2
2
  tabTerminalConnectionId,
3
3
  useSandboxTerminalConnection
4
4
  } from "../chunk-HCOROIRT.js";
5
-
6
- // src/web-react/workspace-terminal-panel.tsx
7
- import { lazy, Suspense } from "react";
8
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
- var TerminalView = lazy(
10
- () => import("@tangle-network/sandbox-ui/terminal").then((m) => ({ default: m.TerminalView }))
11
- );
12
- var TONE_DOT = {
13
- idle: "bg-muted-foreground/50",
14
- connecting: "animate-pulse bg-warning",
15
- connected: "bg-success",
16
- error: "bg-destructive"
17
- };
18
- function defaultStatusDisplay(conn) {
19
- if (conn.error) return { tone: "error", label: "Disconnected" };
20
- if (conn.runtimeUrl && conn.token) return { tone: "connected", label: "Connected" };
21
- if (conn.loading) return { tone: "connecting", label: conn.status === "provisioning" ? "Provisioning\u2026" : "Connecting\u2026" };
22
- return { tone: "idle", label: "Idle" };
23
- }
24
- function WorkspaceTerminalPanel({
25
- connection,
26
- connectionId,
27
- title = "Terminal",
28
- subtitle,
29
- isActive,
30
- onRetry,
31
- statusDisplay,
32
- headerExtra,
33
- className
34
- }) {
35
- const status = (statusDisplay ?? defaultStatusDisplay)(connection);
36
- const apiUrl = connection.runtimeUrl ?? connection.sidecarUrl;
37
- const ready = Boolean(apiUrl && connection.token);
38
- return /* @__PURE__ */ jsxs("div", { className: `flex h-full min-h-0 flex-col overflow-hidden rounded-xl border border-border bg-card ${className ?? ""}`, children: [
39
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-b border-border px-4 py-2.5", children: [
40
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
41
- /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: title }),
42
- subtitle && /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: subtitle })
43
- ] }),
44
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
45
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
46
- /* @__PURE__ */ jsx("span", { className: `h-1.5 w-1.5 rounded-full ${TONE_DOT[status.tone]}`, "aria-hidden": true }),
47
- status.label
48
- ] }),
49
- headerExtra
50
- ] })
51
- ] }),
52
- /* @__PURE__ */ jsx("div", { className: "relative min-h-0 flex-1", children: ready ? /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(TerminalMessage, { children: "Loading terminal\u2026" }), children: /* @__PURE__ */ jsx(
53
- TerminalView,
54
- {
55
- apiUrl,
56
- token: connection.token,
57
- connectionId,
58
- title,
59
- subtitle,
60
- isActive
61
- }
62
- ) }) : /* @__PURE__ */ jsx(TerminalMessage, { children: connection.error ? /* @__PURE__ */ jsxs(Fragment, { children: [
63
- /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: connection.error }),
64
- onRetry && /* @__PURE__ */ jsx(
65
- "button",
66
- {
67
- type: "button",
68
- onClick: onRetry,
69
- className: "mt-3 inline-flex items-center justify-center rounded-lg border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-muted",
70
- children: "Reconnect"
71
- }
72
- )
73
- ] }) : connection.loading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: connection.status === "provisioning" ? "Provisioning sandbox\u2026" : "Connecting\u2026" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
74
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Terminal not connected." }),
75
- onRetry && /* @__PURE__ */ jsx(
76
- "button",
77
- {
78
- type: "button",
79
- onClick: onRetry,
80
- className: "mt-3 inline-flex items-center justify-center rounded-lg border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-muted",
81
- children: "Connect"
82
- }
83
- )
84
- ] }) }) })
85
- ] });
86
- }
87
- function TerminalMessage({ children }) {
88
- return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center p-6 text-center", children });
89
- }
90
5
  export {
91
- WorkspaceTerminalPanel,
92
6
  tabTerminalConnectionId,
93
7
  useSandboxTerminalConnection
94
8
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/web-react/workspace-terminal-panel.tsx"],"sourcesContent":["/**\n * `WorkspaceTerminalPanel` — the shared sandbox-terminal surface: a header with\n * a status badge, connect/provisioning/error states with a retry, and the lazy\n * `TerminalView` (from `@tangle-network/sandbox-ui`) mounted only once the\n * connection is live. creative-agent and gtm-agent each hand-roll a structurally\n * identical panel; only the copy and the status-tone map are app-specific, so\n * those are props and everything else lives here.\n *\n * Pair it with {@link useSandboxTerminalConnection} (the `connection` prop) and\n * {@link tabTerminalConnectionId} (the `connectionId` prop) so reloads restore\n * the same PTY and separate tabs don't evict each other.\n *\n * Styling matches the rest of `web-react`: Tailwind over the shared design\n * tokens; glyphs inline; no icon/UI library.\n */\n\nimport { lazy, Suspense, type ReactNode } from 'react'\n\nimport type { SandboxTerminalConnection } from './sandbox-terminal'\n\nconst TerminalView = lazy(() =>\n import('@tangle-network/sandbox-ui/terminal').then((m) => ({ default: m.TerminalView })),\n)\n\nexport type TerminalStatusTone = 'idle' | 'connecting' | 'connected' | 'error'\n\nexport interface TerminalStatusDisplay {\n tone: TerminalStatusTone\n label: string\n}\n\nexport interface WorkspaceTerminalPanelProps {\n /** Live connection state (from {@link useSandboxTerminalConnection}). */\n connection: SandboxTerminalConnection\n /** Stable per-tab id (from {@link tabTerminalConnectionId}) so the sidecar\n * restores the same PTY across remounts and tabs don't collide. */\n connectionId?: string\n /** Header title. Default \"Terminal\". */\n title?: string\n /** Header subtitle / sandbox label. */\n subtitle?: string\n /** Whether the terminal tab is visible (forwarded to `TerminalView` for fit). */\n isActive?: boolean\n /** Reconnect handler — wire to the hook's `connect`. Shown on idle/error. */\n onRetry?: () => void\n /** Map a `connection.status` to a badge tone + label. The default covers\n * idle/provisioning/running/error; override for app-specific vocabulary. */\n statusDisplay?: (connection: SandboxTerminalConnection) => TerminalStatusDisplay\n /** Extra header content, right-aligned (actions, sandbox id, …). */\n headerExtra?: ReactNode\n className?: string\n}\n\nconst TONE_DOT: Record<TerminalStatusTone, string> = {\n idle: 'bg-muted-foreground/50',\n connecting: 'animate-pulse bg-warning',\n connected: 'bg-success',\n error: 'bg-destructive',\n}\n\nfunction defaultStatusDisplay(conn: SandboxTerminalConnection): TerminalStatusDisplay {\n if (conn.error) return { tone: 'error', label: 'Disconnected' }\n if (conn.runtimeUrl && conn.token) return { tone: 'connected', label: 'Connected' }\n if (conn.loading) return { tone: 'connecting', label: conn.status === 'provisioning' ? 'Provisioning…' : 'Connecting…' }\n return { tone: 'idle', label: 'Idle' }\n}\n\nexport function WorkspaceTerminalPanel({\n connection,\n connectionId,\n title = 'Terminal',\n subtitle,\n isActive,\n onRetry,\n statusDisplay,\n headerExtra,\n className,\n}: WorkspaceTerminalPanelProps): ReactNode {\n const status = (statusDisplay ?? defaultStatusDisplay)(connection)\n const apiUrl = connection.runtimeUrl ?? connection.sidecarUrl\n const ready = Boolean(apiUrl && connection.token)\n\n return (\n <div className={`flex h-full min-h-0 flex-col overflow-hidden rounded-xl border border-border bg-card ${className ?? ''}`}>\n <div className=\"flex items-center justify-between gap-3 border-b border-border px-4 py-2.5\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-foreground\">{title}</p>\n {subtitle && <p className=\"truncate text-xs text-muted-foreground\">{subtitle}</p>}\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span className={`h-1.5 w-1.5 rounded-full ${TONE_DOT[status.tone]}`} aria-hidden />\n {status.label}\n </span>\n {headerExtra}\n </div>\n </div>\n\n <div className=\"relative min-h-0 flex-1\">\n {ready ? (\n <Suspense fallback={<TerminalMessage>Loading terminal…</TerminalMessage>}>\n <TerminalView\n apiUrl={apiUrl as string}\n token={connection.token as string}\n connectionId={connectionId}\n title={title}\n subtitle={subtitle}\n isActive={isActive}\n />\n </Suspense>\n ) : (\n <TerminalMessage>\n {connection.error ? (\n <>\n <p className=\"text-sm text-destructive\">{connection.error}</p>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"mt-3 inline-flex items-center justify-center rounded-lg border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-muted\"\n >\n Reconnect\n </button>\n )}\n </>\n ) : connection.loading ? (\n <p className=\"text-sm text-muted-foreground\">\n {connection.status === 'provisioning' ? 'Provisioning sandbox…' : 'Connecting…'}\n </p>\n ) : (\n <>\n <p className=\"text-sm text-muted-foreground\">Terminal not connected.</p>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"mt-3 inline-flex items-center justify-center rounded-lg border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-muted\"\n >\n Connect\n </button>\n )}\n </>\n )}\n </TerminalMessage>\n )}\n </div>\n </div>\n )\n}\n\nfunction TerminalMessage({ children }: { children: ReactNode }): ReactNode {\n return <div className=\"absolute inset-0 flex flex-col items-center justify-center p-6 text-center\">{children}</div>\n}\n"],"mappings":";;;;;;AAgBA,SAAS,MAAM,gBAAgC;AAqEvC,SA4BM,UA3BJ,KADF;AAjER,IAAM,eAAe;AAAA,EAAK,MACxB,OAAO,qCAAqC,EAAE,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE;AACzF;AA+BA,IAAM,WAA+C;AAAA,EACnD,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,OAAO;AACT;AAEA,SAAS,qBAAqB,MAAwD;AACpF,MAAI,KAAK,MAAO,QAAO,EAAE,MAAM,SAAS,OAAO,eAAe;AAC9D,MAAI,KAAK,cAAc,KAAK,MAAO,QAAO,EAAE,MAAM,aAAa,OAAO,YAAY;AAClF,MAAI,KAAK,QAAS,QAAO,EAAE,MAAM,cAAc,OAAO,KAAK,WAAW,iBAAiB,uBAAkB,mBAAc;AACvH,SAAO,EAAE,MAAM,QAAQ,OAAO,OAAO;AACvC;AAEO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA2C;AACzC,QAAM,UAAU,iBAAiB,sBAAsB,UAAU;AACjE,QAAM,SAAS,WAAW,cAAc,WAAW;AACnD,QAAM,QAAQ,QAAQ,UAAU,WAAW,KAAK;AAEhD,SACE,qBAAC,SAAI,WAAW,wFAAwF,aAAa,EAAE,IACrH;AAAA,yBAAC,SAAI,WAAU,8EACb;AAAA,2BAAC,SAAI,WAAU,WACb;AAAA,4BAAC,OAAE,WAAU,gDAAgD,iBAAM;AAAA,QAClE,YAAY,oBAAC,OAAE,WAAU,0CAA0C,oBAAS;AAAA,SAC/E;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,6BAAC,UAAK,WAAU,2DACd;AAAA,8BAAC,UAAK,WAAW,4BAA4B,SAAS,OAAO,IAAI,CAAC,IAAI,eAAW,MAAC;AAAA,UACjF,OAAO;AAAA,WACV;AAAA,QACC;AAAA,SACH;AAAA,OACF;AAAA,IAEA,oBAAC,SAAI,WAAU,2BACZ,kBACC,oBAAC,YAAS,UAAU,oBAAC,mBAAgB,oCAAiB,GACpD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,OAAO,WAAW;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF,GACF,IAEA,oBAAC,mBACE,qBAAW,QACV,iCACE;AAAA,0BAAC,OAAE,WAAU,4BAA4B,qBAAW,OAAM;AAAA,MACzD,WACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,OAEJ,IACE,WAAW,UACb,oBAAC,OAAE,WAAU,iCACV,qBAAW,WAAW,iBAAiB,+BAA0B,oBACpE,IAEA,iCACE;AAAA,0BAAC,OAAE,WAAU,iCAAgC,qCAAuB;AAAA,MACnE,WACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,OAEJ,GAEJ,GAEJ;AAAA,KACF;AAEJ;AAEA,SAAS,gBAAgB,EAAE,SAAS,GAAuC;AACzE,SAAO,oBAAC,SAAI,WAAU,8EAA8E,UAAS;AAC/G;","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.44.57",
3
+ "version": "0.44.59",
4
4
  "packageManager": "pnpm@11.17.0",
5
5
  "description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
6
6
  "keywords": [
@@ -393,6 +393,11 @@
393
393
  "import": "./dist/vault/lazy.js",
394
394
  "default": "./dist/vault/lazy.js"
395
395
  },
396
+ "./vault/server": {
397
+ "types": "./dist/vault/server.d.ts",
398
+ "import": "./dist/vault/server.js",
399
+ "default": "./dist/vault/server.js"
400
+ },
396
401
  "./brand": {
397
402
  "types": "./dist/brand/index.d.ts",
398
403
  "import": "./dist/brand/index.js",
@@ -1,51 +0,0 @@
1
- /** Define the connection details and status for a sandbox terminal session */
2
- interface SandboxTerminalConnection {
3
- runtimeUrl: string | null;
4
- sidecarUrl: string | null;
5
- token: string | null;
6
- expiresAt: string | null;
7
- status: string;
8
- error: string | null;
9
- loading: boolean;
10
- sandboxId?: string;
11
- }
12
- /** Define the response structure for a sandbox terminal connection including URLs, token, status, and errors */
13
- interface SandboxTerminalConnectionResponse {
14
- runtimeUrl?: string;
15
- sidecarUrl?: string;
16
- token?: string;
17
- expiresAt?: string;
18
- status?: string;
19
- error?: string;
20
- sandboxId?: string;
21
- }
22
- /** Define options for configuring a sandbox terminal connection including workspace ID and connection parameters */
23
- interface UseSandboxTerminalConnectionOptions {
24
- workspaceId: string;
25
- connectionUrl?: string | ((workspaceId: string) => string);
26
- fetcher?: typeof fetch;
27
- provisionPollIntervalMs?: number;
28
- provisionPollTimeoutMs?: number;
29
- tokenRefreshSkewMs?: number;
30
- }
31
- /** Resolve sandbox terminal connection status and provide a method to initiate the connection */
32
- interface UseSandboxTerminalConnectionResult extends SandboxTerminalConnection {
33
- connect: () => Promise<void>;
34
- }
35
- /** Manage and maintain a sandbox terminal connection with automatic polling and token refresh handling */
36
- declare function useSandboxTerminalConnection(opts: UseSandboxTerminalConnectionOptions): UseSandboxTerminalConnectionResult;
37
- /**
38
- * Stable-per-tab, unique-per-client terminal connection id.
39
- *
40
- * Persists in `sessionStorage` so a reload in the same tab reuses the id (the
41
- * sidecar restores the same PTY session via `TerminalView.connectionId`), while
42
- * separate tabs/windows each get a distinct id. Pass the result as
43
- * `TerminalView`'s `connectionId`. Without it (e.g. gtm-agent today) every tab
44
- * shares one connection id and their reconnects evict each other.
45
- *
46
- * Falls back to an ephemeral id when `sessionStorage` is unavailable (SSR,
47
- * privacy mode) — still unique per call, just not reload-stable.
48
- */
49
- declare function tabTerminalConnectionId(storageKey?: string): string;
50
-
51
- export { type SandboxTerminalConnection as S, type UseSandboxTerminalConnectionOptions as U, type SandboxTerminalConnectionResponse as a, type UseSandboxTerminalConnectionResult as b, tabTerminalConnectionId as t, useSandboxTerminalConnection as u };