@naswerks/periscope 1.0.0 → 1.0.1
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.
- package/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/contracts/wire-vectors/bulk_delivered.full.json +21 -0
- package/contracts/wire-vectors/bulk_delivered.minimal.json +21 -0
- package/contracts/wire-vectors/bulk_delivered.without-stat-pair.json +22 -0
- package/contracts/wire-vectors/bulk_failed.declared-reason.json +22 -0
- package/contracts/wire-vectors/bulk_failed.unknown-reason.json +23 -0
- package/contracts/wire-vectors/bulk_request.full.json +21 -0
- package/contracts/wire-vectors/host_configure.set-and-remove.json +28 -0
- package/contracts/wire-vectors/host_configure_result.applied.json +34 -0
- package/contracts/wire-vectors/host_configure_result.refused.json +33 -0
- package/contracts/wire-vectors/link_ack.cursors.json +25 -0
- package/contracts/wire-vectors/link_ack.empty.json +16 -0
- package/contracts/wire-vectors/link_bye.cause.json +16 -0
- package/contracts/wire-vectors/link_hello.empty.json +33 -0
- package/contracts/wire-vectors/link_hello.full.json +44 -0
- package/contracts/wire-vectors/link_ping.nonce.json +16 -0
- package/contracts/wire-vectors/link_ping.unknown-payload-key.json +18 -0
- package/contracts/wire-vectors/link_pong.nonce.json +16 -0
- package/contracts/wire-vectors/link_welcome.empty.json +18 -0
- package/contracts/wire-vectors/link_welcome.full.json +25 -0
- package/contracts/wire-vectors/refused.empty.json +10 -0
- package/contracts/wire-vectors/refused.encode.empty-at.json +18 -0
- package/contracts/wire-vectors/refused.encode.empty-session-id.json +18 -0
- package/contracts/wire-vectors/refused.encode.missing-member.json +22 -0
- package/contracts/wire-vectors/refused.encode.seq-zero.json +18 -0
- package/contracts/wire-vectors/refused.encode.undeclared-configure-refusal.json +34 -0
- package/contracts/wire-vectors/refused.encode.undeclared-wire-refusal.json +27 -0
- package/contracts/wire-vectors/refused.missing-member.json +10 -0
- package/contracts/wire-vectors/refused.not-json.json +10 -0
- package/contracts/wire-vectors/refused.over-limit.json +10 -0
- package/contracts/wire-vectors/refused.seq-zero.json +10 -0
- package/contracts/wire-vectors/refused.unknown-frame.json +10 -0
- package/contracts/wire-vectors/refused.unknown-kind.json +10 -0
- package/contracts/wire-vectors/repository_list.root.json +19 -0
- package/contracts/wire-vectors/repository_list.subdirectory.json +19 -0
- package/contracts/wire-vectors/repository_list_result.entries.json +34 -0
- package/contracts/wire-vectors/repository_list_result.refused.json +24 -0
- package/contracts/wire-vectors/repository_read.head.json +20 -0
- package/contracts/wire-vectors/repository_read_result.refused.json +25 -0
- package/contracts/wire-vectors/repository_read_result.truncated.json +22 -0
- package/contracts/wire-vectors/session_cancel.bare.json +17 -0
- package/contracts/wire-vectors/session_configure.full.json +22 -0
- package/contracts/wire-vectors/session_configure.minimal.json +20 -0
- package/contracts/wire-vectors/session_delta.agent-message.json +27 -0
- package/contracts/wire-vectors/session_list.request.json +18 -0
- package/contracts/wire-vectors/session_list_result.empty.json +21 -0
- package/contracts/wire-vectors/session_list_result.full.json +36 -0
- package/contracts/wire-vectors/session_new.full.json +72 -0
- package/contracts/wire-vectors/session_new.minimal.json +22 -0
- package/contracts/wire-vectors/session_new.request-all-null.json +41 -0
- package/contracts/wire-vectors/session_prompt.empty.json +18 -0
- package/contracts/wire-vectors/session_prompt.multibyte.json +18 -0
- package/contracts/wire-vectors/session_prompt.text.json +18 -0
- package/contracts/wire-vectors/session_prompt.unknown-frame-key.json +20 -0
- package/contracts/wire-vectors/session_prompt.unknown-payload-key.json +20 -0
- package/contracts/wire-vectors/session_update.agent-message.json +26 -0
- package/contracts/wire-vectors/session_update.state-transition-with-activity.json +44 -0
- package/contracts/wire-vectors/session_update.state-transition.json +41 -0
- package/contracts/wire-vectors/session_update.wire-refusal.json +26 -0
- package/contracts/wire-vectors/transcript_failed.declared-reason.json +22 -0
- package/contracts/wire-vectors/transcript_list.from-start.json +19 -0
- package/contracts/wire-vectors/transcript_list.paged.json +19 -0
- package/contracts/wire-vectors/transcript_list_result.entry-without-cwd.json +30 -0
- package/contracts/wire-vectors/transcript_list_result.full.json +29 -0
- package/contracts/wire-vectors/transcript_list_result.last-page.json +29 -0
- package/contracts/wire-vectors/transcript_tail.any-user-entry.json +22 -0
- package/contracts/wire-vectors/transcript_tail.with-needle.json +22 -0
- package/contracts/wire-vectors/transcript_tail_result.absent.json +23 -0
- package/contracts/wire-vectors/transcript_tail_result.found.json +23 -0
- package/contracts/wire-vectors/workspace_list.from-start.json +19 -0
- package/contracts/wire-vectors/workspace_list_result.page.json +48 -0
- package/contracts/wire-vectors/workspace_list_result.refused.json +26 -0
- package/contracts/wire-vectors/workspace_release.by-path-delete-branch.json +22 -0
- package/contracts/wire-vectors/workspace_release.request.json +22 -0
- package/contracts/wire-vectors/workspace_release_bulk.two-entries.json +32 -0
- package/contracts/wire-vectors/workspace_release_bulk_result.mixed.json +37 -0
- package/contracts/wire-vectors/workspace_release_result.partial.json +26 -0
- package/contracts/wire-vectors/workspace_release_result.refused.json +26 -0
- package/contracts/wire-vectors/workspace_release_result.released.json +23 -0
- package/package.json +6 -1
- package/src/bin/command.ts +198 -0
- package/src/bin/config.ts +104 -0
- package/src/bin/login.ts +149 -0
- package/src/bin/main.ts +204 -0
- package/src/bin/pair.ts +246 -0
- package/src/bin/periscope.ts +3 -0
- package/src/bin/posture.ts +232 -0
- package/src/bin/reconfigure.ts +266 -0
- package/src/bin/serve.ts +615 -0
- package/src/bin/status.ts +61 -0
- package/src/bin/workspaces.ts +280 -0
- package/src/control/backoff.ts +40 -0
- package/src/control/codec.ts +557 -0
- package/src/control/credential.ts +33 -0
- package/src/control/frames.ts +1586 -0
- package/src/control/link-state.ts +67 -0
- package/src/control/link.ts +894 -0
- package/src/control/queue.ts +248 -0
- package/src/control/seq.ts +76 -0
- package/src/control/stream-routing.ts +216 -0
- package/src/control/stream.ts +149 -0
- package/src/core/async-queue.ts +63 -0
- package/src/core/failure.ts +61 -0
- package/src/core/index.ts +22 -0
- package/src/core/keyed-turns.ts +47 -0
- package/src/core/paths.ts +84 -0
- package/src/core/refusal.ts +430 -0
- package/src/core/result.ts +27 -0
- package/src/core/time.ts +34 -0
- package/src/core/vocab.ts +44 -0
- package/src/core/workspace-id.ts +11 -0
- package/src/gate/command.ts +617 -0
- package/src/gate/decision.ts +173 -0
- package/src/gate/escalate.ts +164 -0
- package/src/gate/gate.ts +635 -0
- package/src/gate/index.ts +53 -0
- package/src/gate/jail.ts +210 -0
- package/src/gate/local.ts +115 -0
- package/src/gate/outcome.ts +164 -0
- package/src/gate/shell.ts +295 -0
- package/src/host/agent-process.ts +694 -0
- package/src/host/bulk-post.ts +166 -0
- package/src/host/claude-transcripts.ts +404 -0
- package/src/host/config-file.ts +246 -0
- package/src/host/git-facts.ts +119 -0
- package/src/host/hooks.ts +102 -0
- package/src/host/host.ts +1901 -0
- package/src/host/index.ts +92 -0
- package/src/host/link-state-file.ts +79 -0
- package/src/host/loopback.ts +177 -0
- package/src/host/machine.ts +27 -0
- package/src/host/mcp-server.ts +66 -0
- package/src/host/package-facts.ts +25 -0
- package/src/host/paired-credential-store.ts +140 -0
- package/src/host/paths.ts +175 -0
- package/src/host/repository-read.ts +245 -0
- package/src/host/session-store.ts +133 -0
- package/src/host/sign-in.ts +352 -0
- package/src/host/telemetry.ts +105 -0
- package/src/host/token-cache.ts +214 -0
- package/src/host/transcript-fs.ts +110 -0
- package/src/host/wire-request.ts +285 -0
- package/src/host/workspace-fs.ts +90 -0
- package/src/host/workspace-trust.ts +92 -0
- package/src/identity/authorize.ts +127 -0
- package/src/identity/config.ts +227 -0
- package/src/identity/credential.ts +176 -0
- package/src/identity/device-code.ts +217 -0
- package/src/identity/index.ts +84 -0
- package/src/identity/mode.ts +161 -0
- package/src/identity/paired-credential.ts +89 -0
- package/src/identity/pkce.ts +113 -0
- package/src/identity/store.ts +98 -0
- package/src/identity/token.ts +203 -0
- package/src/index.ts +140 -0
- package/src/mcp/descriptor.ts +75 -0
- package/src/mcp/index.ts +23 -0
- package/src/mcp/schema.ts +214 -0
- package/src/mcp/server.ts +180 -0
- package/src/persistence/entry.ts +142 -0
- package/src/persistence/index.ts +47 -0
- package/src/persistence/key.ts +77 -0
- package/src/persistence/mirror.ts +118 -0
- package/src/persistence/receipt.ts +182 -0
- package/src/persistence/retention.ts +118 -0
- package/src/persistence/store.ts +183 -0
- package/src/persistence/transition-log.ts +176 -0
- package/src/protocol.ts +215 -0
- package/src/sessions/index.ts +27 -0
- package/src/sessions/registry.ts +392 -0
- package/src/sessions/session.ts +440 -0
- package/src/sessions/spawn-env.ts +318 -0
- package/src/state/coverage.ts +360 -0
- package/src/state/index.ts +58 -0
- package/src/state/machine.ts +359 -0
- package/src/state/model.ts +417 -0
- package/src/state/observer.ts +499 -0
- package/src/state/reporter.ts +60 -0
- package/src/state/store.ts +157 -0
- package/src/telemetry/index.ts +10 -0
- package/src/telemetry/usage.ts +117 -0
- package/src/workspace/git-worktree.ts +551 -0
- package/src/workspace/index.ts +17 -0
- package/src/workspace/plain-dir.ts +132 -0
- package/src/workspace/provider.ts +159 -0
- package/src/workspace/worktree-porcelain.ts +118 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable transition log — the only place a gate decision is stored durably.
|
|
3
|
+
*
|
|
4
|
+
* Read this before adding any cleanup, prune or tidy-up. `state/store.ts` retains transitions in
|
|
5
|
+
* a bounded in-memory ring that actively drops its oldest once a session outruns the window, and
|
|
6
|
+
* its own header says durability belongs here. Every recorded transition also reaches the link —
|
|
7
|
+
* `forwardSession` in `control/stream.ts` subscribes to the machine and forwards whichever lane
|
|
8
|
+
* produced it — but the link is a stream, not a record: once a frame has been sent, nothing in
|
|
9
|
+
* this package can read it back. So every local refusal this host decides — a path-jail denial, a
|
|
10
|
+
* boundary command, a credential-path read, an unconvertible tool descriptor — is held in-process
|
|
11
|
+
* only by a ring that will eventually overwrite it. This file is what makes such a decision
|
|
12
|
+
* readable afterwards. A tidy-up here does not lose a log line; it loses the only stored evidence
|
|
13
|
+
* that the package's headline feature ever ran.
|
|
14
|
+
*
|
|
15
|
+
* Append-only, and an abandoned entry is marked rather than erased. "This session sat in
|
|
16
|
+
* `tool:Bash` for forty minutes" is the most useful sentence this system can produce, and a
|
|
17
|
+
* reconciler that quietly closed the entry would destroy exactly that. So `markAbandoned` writes a
|
|
18
|
+
* new record carrying the reason; it never rewrites or removes the one that is already stored.
|
|
19
|
+
*
|
|
20
|
+
* `cause` survives the round trip or the read refuses. A transition read back without the event
|
|
21
|
+
* that produced it would leave a reader with a state change and no way to know what caused it,
|
|
22
|
+
* which is exactly the inference the declared model exists to eliminate. So the encoder writes all
|
|
23
|
+
* three parts of a cause and the decoder refuses an entry that lost any of them, rather than
|
|
24
|
+
* substituting a plausible default.
|
|
25
|
+
*/
|
|
26
|
+
import type { OpenEntry, SessionTransition } from '../state/model.js';
|
|
27
|
+
import { isCauseEvent, isCauseKind } from '../state/model.js';
|
|
28
|
+
import type { Result } from '../core/result.js';
|
|
29
|
+
import { ok, refuse } from '../core/result.js';
|
|
30
|
+
import type { TranscriptEntry } from './entry.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The entry `type` a stored transition carries.
|
|
34
|
+
*
|
|
35
|
+
* Namespaced so it cannot collide with a transcript entry. These records may share a store with
|
|
36
|
+
* mirrored transcript lines, whose `type` values are the CLI's. A prefix nothing in that vocabulary
|
|
37
|
+
* uses keeps the two readable side by side and keeps a reader of either from mistaking one for the
|
|
38
|
+
* other.
|
|
39
|
+
*/
|
|
40
|
+
export const TRANSITION_ENTRY_TYPE = 'periscope.transition';
|
|
41
|
+
|
|
42
|
+
/** The entry `type` an abandonment mark carries. A record in its own right, never an edit. */
|
|
43
|
+
export const ABANDONED_ENTRY_TYPE = 'periscope.entry_abandoned';
|
|
44
|
+
|
|
45
|
+
/** Encode one transition as a storable entry. Lossless for everything a reader branches on. */
|
|
46
|
+
export function encodeTransition(transition: SessionTransition): TranscriptEntry {
|
|
47
|
+
return {
|
|
48
|
+
type: TRANSITION_ENTRY_TYPE,
|
|
49
|
+
uuid: transitionUuid(transition),
|
|
50
|
+
timestamp: transition.at,
|
|
51
|
+
sessionId: transition.sessionId,
|
|
52
|
+
seq: transition.seq,
|
|
53
|
+
from: transition.from,
|
|
54
|
+
to: transition.to,
|
|
55
|
+
activity: transition.activity,
|
|
56
|
+
entryId: transition.entryId,
|
|
57
|
+
cause: { kind: transition.cause.kind, event: transition.cause.event, detail: transition.cause.detail },
|
|
58
|
+
where: transition.where,
|
|
59
|
+
correlationId: transition.correlationId,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Read a stored transition back.
|
|
65
|
+
*
|
|
66
|
+
* A lost or unrecognised cause is a refusal. Both halves are checked independently — the same way
|
|
67
|
+
* the machine validates them — because a cause that survived as a shape but not as a declared value
|
|
68
|
+
* is a record a reader would branch on wrongly.
|
|
69
|
+
*/
|
|
70
|
+
export function decodeTransition(entry: TranscriptEntry): Result<SessionTransition> {
|
|
71
|
+
if (entry.type !== TRANSITION_ENTRY_TYPE) {
|
|
72
|
+
return refuse<SessionTransition>(
|
|
73
|
+
'transcript-entry-malformed',
|
|
74
|
+
`not a transition entry: type ${entry.type}`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const cause = entry['cause'];
|
|
79
|
+
if (cause === null || typeof cause !== 'object' || Array.isArray(cause)) {
|
|
80
|
+
return refuse<SessionTransition>('transcript-entry-malformed', 'the transition carries no cause');
|
|
81
|
+
}
|
|
82
|
+
const { kind, event, detail } = cause as Record<string, unknown>;
|
|
83
|
+
if (typeof kind !== 'string' || !isCauseKind(kind)) {
|
|
84
|
+
return refuse<SessionTransition>(
|
|
85
|
+
'transcript-entry-malformed',
|
|
86
|
+
`the cause kind is not a declared kind: ${String(kind)}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (typeof event !== 'string' || !isCauseEvent(event)) {
|
|
90
|
+
return refuse<SessionTransition>(
|
|
91
|
+
'transcript-entry-malformed',
|
|
92
|
+
`the cause event is not a declared event: ${String(event)}`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const seq = entry['seq'];
|
|
97
|
+
if (typeof seq !== 'number' || !Number.isFinite(seq)) {
|
|
98
|
+
return refuse<SessionTransition>('transcript-entry-malformed', 'the transition carries no seq');
|
|
99
|
+
}
|
|
100
|
+
const where = entry['where'];
|
|
101
|
+
if (where === null || typeof where !== 'object') {
|
|
102
|
+
return refuse<SessionTransition>('transcript-entry-malformed', 'the transition carries no where');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return ok({
|
|
106
|
+
sessionId: (entry['sessionId'] as string | null) ?? null,
|
|
107
|
+
seq,
|
|
108
|
+
at: typeof entry.timestamp === 'string' ? entry.timestamp : '',
|
|
109
|
+
from: entry['from'] as SessionTransition['from'],
|
|
110
|
+
to: entry['to'] as SessionTransition['to'],
|
|
111
|
+
activity: (entry['activity'] as SessionTransition['activity']) ?? null,
|
|
112
|
+
entryId: (entry['entryId'] as string | null) ?? null,
|
|
113
|
+
cause: { kind, event, detail: typeof detail === 'string' ? detail : '' },
|
|
114
|
+
where: where as SessionTransition['where'],
|
|
115
|
+
correlationId: (entry['correlationId'] as string | null) ?? null,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* A record that an open entry was abandoned.
|
|
121
|
+
*
|
|
122
|
+
* This is an append, not an edit, and that is the whole design. The entry it refers to stays
|
|
123
|
+
* exactly as it was written, with the time it opened and the cause that opened it. A reader
|
|
124
|
+
* assembling the two sees "opened at X, still open at Y, marked abandoned because Z" — which is the
|
|
125
|
+
* signal. Rewriting the original would leave "closed", which is the signal's opposite and is
|
|
126
|
+
* indistinguishable from an ordinary completion.
|
|
127
|
+
*/
|
|
128
|
+
export function markAbandoned(entry: OpenEntry, at: string, reason: string): TranscriptEntry {
|
|
129
|
+
return {
|
|
130
|
+
type: ABANDONED_ENTRY_TYPE,
|
|
131
|
+
uuid: `abandoned:${entry.entryId}:${at}`,
|
|
132
|
+
timestamp: at,
|
|
133
|
+
entryId: entry.entryId,
|
|
134
|
+
activity: entry.activity,
|
|
135
|
+
lane: entry.lane,
|
|
136
|
+
openedAt: entry.openedAt,
|
|
137
|
+
agentId: entry.agentId,
|
|
138
|
+
reason,
|
|
139
|
+
cause: { kind: entry.cause.kind, event: entry.cause.event, detail: entry.cause.detail },
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Every stored transition in an entry list, in stored order. Non-transition entries are ignored. */
|
|
144
|
+
export function transitionsIn(entries: readonly TranscriptEntry[]): Result<SessionTransition[]> {
|
|
145
|
+
const found: SessionTransition[] = [];
|
|
146
|
+
for (const entry of entries) {
|
|
147
|
+
if (entry.type !== TRANSITION_ENTRY_TYPE) continue;
|
|
148
|
+
const decoded = decodeTransition(entry);
|
|
149
|
+
if (!decoded.ok) return refuse<SessionTransition[]>(decoded.refusal.reason, decoded.refusal.detail);
|
|
150
|
+
found.push(decoded.value);
|
|
151
|
+
}
|
|
152
|
+
return ok(found);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Every abandonment mark in an entry list, by the entry id it refers to. */
|
|
156
|
+
export function abandonmentsIn(entries: readonly TranscriptEntry[]): Map<string, TranscriptEntry> {
|
|
157
|
+
const marks = new Map<string, TranscriptEntry>();
|
|
158
|
+
for (const entry of entries) {
|
|
159
|
+
if (entry.type !== ABANDONED_ENTRY_TYPE) continue;
|
|
160
|
+
const entryId = entry['entryId'];
|
|
161
|
+
if (typeof entryId === 'string') marks.set(entryId, entry);
|
|
162
|
+
}
|
|
163
|
+
return marks;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* A stable uuid for one transition, so a re-append is deduplicated rather than doubled.
|
|
168
|
+
*
|
|
169
|
+
* Derived, not minted. The adapter contract treats `uuid` as an idempotency key, and a mirror
|
|
170
|
+
* retries. A random id would make every retry a new row, so the same transition written twice must
|
|
171
|
+
* carry the same id — and a session's `seq` is dense from 1 per machine, which makes the pair
|
|
172
|
+
* unique without inventing anything.
|
|
173
|
+
*/
|
|
174
|
+
export function transitionUuid(transition: SessionTransition): string {
|
|
175
|
+
return `transition:${transition.sessionId ?? 'unidentified'}:${transition.correlationId ?? '-'}:${transition.seq}`;
|
|
176
|
+
}
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wire contract, on its own subpath — `@naswerks/periscope/protocol`.
|
|
3
|
+
*
|
|
4
|
+
* This is the only import path into the package that structurally cannot reach `src/host/`, so a
|
|
5
|
+
* controller written against it cannot transitively acquire `node:fs`, `node:child_process` or
|
|
6
|
+
* `node:os`, and does not pull the Agent SDK. That turns the package's blast-radius claim into
|
|
7
|
+
* something a consumer can verify at the package boundary instead of taking on trust from the
|
|
8
|
+
* package's own test suite. Pinned by src/pins/protocol-closure.test.ts.
|
|
9
|
+
*
|
|
10
|
+
* The condition that keeps it true: `exports` in package.json has exactly these two entries and no
|
|
11
|
+
* catch-all `"./*"`. A third subpath re-opens the boundary and is a decision, not a refactor.
|
|
12
|
+
*/
|
|
13
|
+
export type {
|
|
14
|
+
AgentMessageUpdate,
|
|
15
|
+
BulkDelivered,
|
|
16
|
+
BulkFailed,
|
|
17
|
+
BulkRequest,
|
|
18
|
+
ControlFrame,
|
|
19
|
+
ControlPayload,
|
|
20
|
+
ControlPayloadKind,
|
|
21
|
+
Frame,
|
|
22
|
+
HostConfiguration,
|
|
23
|
+
HostConfigure,
|
|
24
|
+
HostConfigureEntry,
|
|
25
|
+
HostConfigureResult,
|
|
26
|
+
JsonObject,
|
|
27
|
+
JsonValue,
|
|
28
|
+
LinkAck,
|
|
29
|
+
LinkBye,
|
|
30
|
+
LinkHello,
|
|
31
|
+
LinkPing,
|
|
32
|
+
LinkPong,
|
|
33
|
+
LinkWelcome,
|
|
34
|
+
ProtocolRange,
|
|
35
|
+
ReadRefusal,
|
|
36
|
+
SessionCancel,
|
|
37
|
+
SessionConfigure,
|
|
38
|
+
SessionCursor,
|
|
39
|
+
SessionDelta,
|
|
40
|
+
SessionFrame,
|
|
41
|
+
SessionList,
|
|
42
|
+
SessionListEntry,
|
|
43
|
+
SessionListResult,
|
|
44
|
+
SessionNew,
|
|
45
|
+
SessionNewEnv,
|
|
46
|
+
SessionNewGate,
|
|
47
|
+
SessionNewPlugin,
|
|
48
|
+
SessionNewRequest,
|
|
49
|
+
SessionPayload,
|
|
50
|
+
SessionPayloadKind,
|
|
51
|
+
SessionPrompt,
|
|
52
|
+
SessionUpdate,
|
|
53
|
+
SessionUpdateBody,
|
|
54
|
+
StateTransitionUpdate,
|
|
55
|
+
TranscriptFailed,
|
|
56
|
+
TranscriptList,
|
|
57
|
+
TranscriptListEntry,
|
|
58
|
+
TranscriptListResult,
|
|
59
|
+
TranscriptTail,
|
|
60
|
+
TranscriptTailResult,
|
|
61
|
+
WireRefusal,
|
|
62
|
+
WireRefusalUpdate,
|
|
63
|
+
RepositoryEntry,
|
|
64
|
+
RepositoryList,
|
|
65
|
+
RepositoryListResult,
|
|
66
|
+
RepositoryRead,
|
|
67
|
+
RepositoryReadResult,
|
|
68
|
+
WorkspaceList,
|
|
69
|
+
WorkspaceListEntry,
|
|
70
|
+
WorkspaceListResult,
|
|
71
|
+
WorkspaceRelease,
|
|
72
|
+
WorkspaceReleaseBulk,
|
|
73
|
+
WorkspaceReleaseBulkResult,
|
|
74
|
+
WorkspaceReleaseEntry,
|
|
75
|
+
WorkspaceReleaseEntryResult,
|
|
76
|
+
WorkspaceReleaseFlags,
|
|
77
|
+
WorkspaceReleaseResult,
|
|
78
|
+
} from './control/frames.js';
|
|
79
|
+
|
|
80
|
+
// The declared state model travels with the wire: a `session_update` carries a transition, so a
|
|
81
|
+
// consumer parsing frames needs the vocabulary to read one. This is the model's public home — the
|
|
82
|
+
// controller reads these types and builds whatever its own conventions mean on top of them.
|
|
83
|
+
export type {
|
|
84
|
+
ActivityKind,
|
|
85
|
+
AgedEntry,
|
|
86
|
+
CauseEvent,
|
|
87
|
+
CauseKind,
|
|
88
|
+
EntryLane,
|
|
89
|
+
HookEventName,
|
|
90
|
+
OpenEntry,
|
|
91
|
+
SessionActivity,
|
|
92
|
+
SessionSnapshot,
|
|
93
|
+
SessionState,
|
|
94
|
+
SessionTransition,
|
|
95
|
+
TransitionCause,
|
|
96
|
+
TransitionWhere,
|
|
97
|
+
} from './state/model.js';
|
|
98
|
+
export {
|
|
99
|
+
ACTIVITY_KINDS,
|
|
100
|
+
CAUSE_KINDS,
|
|
101
|
+
HOOK_EVENTS,
|
|
102
|
+
HOST_ACTIVITY_KINDS,
|
|
103
|
+
SDK_ACTIVITY_KINDS,
|
|
104
|
+
SESSION_STATES,
|
|
105
|
+
formatActivity,
|
|
106
|
+
isCauseEvent,
|
|
107
|
+
isCauseKind,
|
|
108
|
+
sameActivity,
|
|
109
|
+
} from './state/model.js';
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
DROPPABLE_KINDS,
|
|
113
|
+
MAX_CONFIGURATION_VALUE_LENGTH,
|
|
114
|
+
MAX_BULK_RELEASES,
|
|
115
|
+
MAX_CONFIGURE_ENTRIES,
|
|
116
|
+
MAX_FRAME_BYTES,
|
|
117
|
+
MAX_REPOSITORY_ENTRIES,
|
|
118
|
+
MAX_REPOSITORY_READ_BYTES,
|
|
119
|
+
PROTOCOL_VERSION,
|
|
120
|
+
PROTOCOL_VERSION_MIN,
|
|
121
|
+
TRANSCRIPT_PAGE_SIZE,
|
|
122
|
+
TRANSCRIPT_WHAT_PREFIX,
|
|
123
|
+
WORKSPACE_PAGE_SIZE,
|
|
124
|
+
agentMessageDelta,
|
|
125
|
+
agentMessageUpdate,
|
|
126
|
+
bulkDelivered,
|
|
127
|
+
frameId,
|
|
128
|
+
hostConfigure,
|
|
129
|
+
hostConfigureResult,
|
|
130
|
+
isControlFrame,
|
|
131
|
+
isDroppable,
|
|
132
|
+
isSessionFrame,
|
|
133
|
+
readAgentMessage,
|
|
134
|
+
readRefusal,
|
|
135
|
+
readStateTransition,
|
|
136
|
+
readWireRefusal,
|
|
137
|
+
repositoryList,
|
|
138
|
+
repositoryListResult,
|
|
139
|
+
repositoryRead,
|
|
140
|
+
repositoryReadResult,
|
|
141
|
+
sessionList,
|
|
142
|
+
sessionListResult,
|
|
143
|
+
sessionNew,
|
|
144
|
+
sessionNewRequest,
|
|
145
|
+
stateTransitionUpdate,
|
|
146
|
+
transcriptFailed,
|
|
147
|
+
transcriptList,
|
|
148
|
+
transcriptListResult,
|
|
149
|
+
transcriptTail,
|
|
150
|
+
transcriptTailResult,
|
|
151
|
+
unsetHostConfiguration,
|
|
152
|
+
wireRefusalUpdate,
|
|
153
|
+
workspaceList,
|
|
154
|
+
workspaceListResult,
|
|
155
|
+
workspaceRelease,
|
|
156
|
+
workspaceReleaseBulk,
|
|
157
|
+
workspaceReleaseBulkResult,
|
|
158
|
+
workspaceReleaseEntryResult,
|
|
159
|
+
workspaceReleaseResult,
|
|
160
|
+
} from './control/frames.js';
|
|
161
|
+
|
|
162
|
+
// `control/stream-routing.ts` is not exported here, deliberately. It is keyed off the SDK's own
|
|
163
|
+
// message union, so it reaches `host/` and would drag the privileged module into this subpath's
|
|
164
|
+
// closure — the one thing this file exists to prevent. What a wire consumer needs is already here:
|
|
165
|
+
// `DROPPABLE_KINDS` and `isDroppable` say which payload kinds may be lost. The routing table is the
|
|
166
|
+
// host's emission policy, and it ships from the main barrel.
|
|
167
|
+
|
|
168
|
+
export { decode, encode } from './control/codec.js';
|
|
169
|
+
|
|
170
|
+
export type { InboundCheck } from './control/seq.js';
|
|
171
|
+
export { SeqTracker } from './control/seq.js';
|
|
172
|
+
|
|
173
|
+
// The refusal vocabulary travels with the wire: a `bulk_failed` frame carries one, so a consumer
|
|
174
|
+
// parsing frames needs the type to read it.
|
|
175
|
+
export type { Refusal, RefusalReason } from './core/refusal.js';
|
|
176
|
+
export { REFUSAL_REASONS, isRefusalReason } from './core/refusal.js';
|
|
177
|
+
|
|
178
|
+
export type { Result } from './core/result.js';
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// The permission surface — the one thing a controller is required to implement, so the one thing
|
|
182
|
+
// this subpath must be able to type.
|
|
183
|
+
//
|
|
184
|
+
// The package's headline safety claim is that `@naswerks/periscope/protocol` structurally cannot reach
|
|
185
|
+
// `src/host/`. Without these re-exports, a stranger writing a controller would have to import the
|
|
186
|
+
// permission types from the main barrel — forfeiting the boundary the subpath exists to provide —
|
|
187
|
+
// or hand-copy them, or resort to `ReturnType<typeof …>`. The one surface they cannot avoid
|
|
188
|
+
// implementing would be the one surface the safe import path did not carry.
|
|
189
|
+
//
|
|
190
|
+
// These cost the boundary nothing, which is why they can ship here. `gate/decision.ts` has zero
|
|
191
|
+
// imports and `gate/escalate.ts` imports only `./decision.js`; both are type-only from this file's
|
|
192
|
+
// point of view. `src/pins/protocol-closure.test.ts` is the arbiter, and it stays green: no `host/`
|
|
193
|
+
// reach, no `node:` builtin, no Node-only global.
|
|
194
|
+
//
|
|
195
|
+
// Note what is not here, because the omission is deliberate. `SessionStore` and `SessionKey` are
|
|
196
|
+
// re-exports of the Agent SDK's own types (`host/session-store.ts`), and `readTurnSpend` is a
|
|
197
|
+
// runtime function in `host/telemetry.ts`. Routing any of the three through here would either drag
|
|
198
|
+
// `host/` into this closure or make the wire subpath import the Agent SDK — contradicting this
|
|
199
|
+
// file's own header claim that it does not. Whether the wire should grow its own session/spend
|
|
200
|
+
// types, independent of the SDK's, is a wire-vocabulary decision and belongs to whoever designs
|
|
201
|
+
// the API. It is deliberately not taken here.
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
export type { Decider, Decision, DecisionReading, DecisionRequest } from './gate/decision.js';
|
|
205
|
+
export { describeRaw, readDecision } from './gate/decision.js';
|
|
206
|
+
|
|
207
|
+
export type { EscalationOptions, EscalationResponse, EscalationTransport } from './gate/escalate.js';
|
|
208
|
+
|
|
209
|
+
// `EscalationOptions` names a credential, so the credential's own types have to be reachable from
|
|
210
|
+
// the same import path or the option is untypeable by the consumer it exists for. They cost the
|
|
211
|
+
// closure nothing: `control/credential.ts` imports `core/result.ts` and nothing else — no `host/`
|
|
212
|
+
// reach, no `node:` builtin, no Node-only global — so `pins/protocol-closure.test.ts` stays the
|
|
213
|
+
// arbiter and stays green. The one implementation is deliberately not re-exported here: it refuses
|
|
214
|
+
// by name, and a wire consumer types against the interface rather than instantiating the package's.
|
|
215
|
+
export type { Authorization, ControllerCredential } from './control/credential.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** The agent session lifecycle and the registry of live sessions. */
|
|
2
|
+
export type { EnvSource, SpawnEnvPolicy } from './spawn-env.js';
|
|
3
|
+
export {
|
|
4
|
+
DECLARED_EXACT_KEYS,
|
|
5
|
+
DECLARED_PREFIXES,
|
|
6
|
+
DECLARED_SUFFIXES,
|
|
7
|
+
STRIPPED_HOST_SESSION_KEYS,
|
|
8
|
+
composeSpawnEnv,
|
|
9
|
+
isDeclaredSpawnEnvKey,
|
|
10
|
+
redactProxyCredential,
|
|
11
|
+
} from './spawn-env.js';
|
|
12
|
+
|
|
13
|
+
export type {
|
|
14
|
+
HostedSessionFacts,
|
|
15
|
+
SessionDegrade,
|
|
16
|
+
SessionDegradeListener,
|
|
17
|
+
SessionEndCause,
|
|
18
|
+
SessionEndListener,
|
|
19
|
+
SessionEnded,
|
|
20
|
+
SessionLifecycle,
|
|
21
|
+
SessionListener,
|
|
22
|
+
Unsubscribe,
|
|
23
|
+
} from './session.js';
|
|
24
|
+
export { SESSION_END_CAUSES, HostedSession } from './session.js';
|
|
25
|
+
|
|
26
|
+
export type { SessionRegistryOptions, SessionRequest } from './registry.js';
|
|
27
|
+
export { SessionRegistry } from './registry.js';
|