@jini-ai/daemon 0.1.0
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/LICENSE +201 -0
- package/dist/agent-executor.d.ts +458 -0
- package/dist/agent-executor.d.ts.map +1 -0
- package/dist/agent-executor.js +1277 -0
- package/dist/agent-executor.js.map +1 -0
- package/dist/artifacts/index.d.ts +14 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +14 -0
- package/dist/artifacts/index.js.map +1 -0
- package/dist/artifacts/manifest.d.ts +90 -0
- package/dist/artifacts/manifest.d.ts.map +1 -0
- package/dist/artifacts/manifest.js +229 -0
- package/dist/artifacts/manifest.js.map +1 -0
- package/dist/artifacts/publication-guard.d.ts +28 -0
- package/dist/artifacts/publication-guard.d.ts.map +1 -0
- package/dist/artifacts/publication-guard.js +77 -0
- package/dist/artifacts/publication-guard.js.map +1 -0
- package/dist/artifacts/runtime-compat.d.ts +37 -0
- package/dist/artifacts/runtime-compat.d.ts.map +1 -0
- package/dist/artifacts/runtime-compat.js +33 -0
- package/dist/artifacts/runtime-compat.js.map +1 -0
- package/dist/artifacts/store.d.ts +85 -0
- package/dist/artifacts/store.d.ts.map +1 -0
- package/dist/artifacts/store.js +92 -0
- package/dist/artifacts/store.js.map +1 -0
- package/dist/artifacts/stub-guard.d.ts +73 -0
- package/dist/artifacts/stub-guard.d.ts.map +1 -0
- package/dist/artifacts/stub-guard.js +198 -0
- package/dist/artifacts/stub-guard.js.map +1 -0
- package/dist/artifacts/text-suppression.d.ts +68 -0
- package/dist/artifacts/text-suppression.d.ts.map +1 -0
- package/dist/artifacts/text-suppression.js +184 -0
- package/dist/artifacts/text-suppression.js.map +1 -0
- package/dist/close-status.d.ts +86 -0
- package/dist/close-status.d.ts.map +1 -0
- package/dist/close-status.js +91 -0
- package/dist/close-status.js.map +1 -0
- package/dist/continuation/continuation-transport.d.ts +41 -0
- package/dist/continuation/continuation-transport.d.ts.map +1 -0
- package/dist/continuation/continuation-transport.js +18 -0
- package/dist/continuation/continuation-transport.js.map +1 -0
- package/dist/continuation/index.d.ts +4 -0
- package/dist/continuation/index.d.ts.map +1 -0
- package/dist/continuation/index.js +4 -0
- package/dist/continuation/index.js.map +1 -0
- package/dist/continuation/journal.d.ts +37 -0
- package/dist/continuation/journal.d.ts.map +1 -0
- package/dist/continuation/journal.js +23 -0
- package/dist/continuation/journal.js.map +1 -0
- package/dist/continuation/run-start-handler.d.ts +75 -0
- package/dist/continuation/run-start-handler.d.ts.map +1 -0
- package/dist/continuation/run-start-handler.js +32 -0
- package/dist/continuation/run-start-handler.js.map +1 -0
- package/dist/delegated-tool-bridge.d.ts +53 -0
- package/dist/delegated-tool-bridge.d.ts.map +1 -0
- package/dist/delegated-tool-bridge.js +92 -0
- package/dist/delegated-tool-bridge.js.map +1 -0
- package/dist/event-log.d.ts +134 -0
- package/dist/event-log.d.ts.map +1 -0
- package/dist/event-log.js +108 -0
- package/dist/event-log.js.map +1 -0
- package/dist/frontend-capability-tools.d.ts +102 -0
- package/dist/frontend-capability-tools.d.ts.map +1 -0
- package/dist/frontend-capability-tools.js +68 -0
- package/dist/frontend-capability-tools.js.map +1 -0
- package/dist/frontend-session-registry.d.ts +127 -0
- package/dist/frontend-session-registry.d.ts.map +1 -0
- package/dist/frontend-session-registry.js +180 -0
- package/dist/frontend-session-registry.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/legacy-data-migration.d.ts +104 -0
- package/dist/legacy-data-migration.d.ts.map +1 -0
- package/dist/legacy-data-migration.js +239 -0
- package/dist/legacy-data-migration.js.map +1 -0
- package/dist/routines/index.d.ts +12 -0
- package/dist/routines/index.d.ts.map +1 -0
- package/dist/routines/index.js +12 -0
- package/dist/routines/index.js.map +1 -0
- package/dist/routines/routine-store.d.ts +77 -0
- package/dist/routines/routine-store.d.ts.map +1 -0
- package/dist/routines/routine-store.js +162 -0
- package/dist/routines/routine-store.js.map +1 -0
- package/dist/routines/schedule.d.ts +72 -0
- package/dist/routines/schedule.d.ts.map +1 -0
- package/dist/routines/schedule.js +263 -0
- package/dist/routines/schedule.js.map +1 -0
- package/dist/routines/scheduler.d.ts +34 -0
- package/dist/routines/scheduler.d.ts.map +1 -0
- package/dist/routines/scheduler.js +330 -0
- package/dist/routines/scheduler.js.map +1 -0
- package/dist/routines/types.d.ts +131 -0
- package/dist/routines/types.d.ts.map +1 -0
- package/dist/routines/types.js +9 -0
- package/dist/routines/types.js.map +1 -0
- package/dist/run/core/failure-taxonomy.d.ts +27 -0
- package/dist/run/core/failure-taxonomy.d.ts.map +1 -0
- package/dist/run/core/failure-taxonomy.js +16 -0
- package/dist/run/core/failure-taxonomy.js.map +1 -0
- package/dist/run/core/index.d.ts +5 -0
- package/dist/run/core/index.d.ts.map +1 -0
- package/dist/run/core/index.js +5 -0
- package/dist/run/core/index.js.map +1 -0
- package/dist/run/core/result.d.ts +25 -0
- package/dist/run/core/result.d.ts.map +1 -0
- package/dist/run/core/result.js +52 -0
- package/dist/run/core/result.js.map +1 -0
- package/dist/run/core/retry.d.ts +106 -0
- package/dist/run/core/retry.d.ts.map +1 -0
- package/dist/run/core/retry.js +210 -0
- package/dist/run/core/retry.js.map +1 -0
- package/dist/run/diagnostics/diagnostics.d.ts +86 -0
- package/dist/run/diagnostics/diagnostics.d.ts.map +1 -0
- package/dist/run/diagnostics/diagnostics.js +211 -0
- package/dist/run/diagnostics/diagnostics.js.map +1 -0
- package/dist/run/diagnostics/index.d.ts +3 -0
- package/dist/run/diagnostics/index.d.ts.map +1 -0
- package/dist/run/diagnostics/index.js +3 -0
- package/dist/run/diagnostics/index.js.map +1 -0
- package/dist/run/index.d.ts +16 -0
- package/dist/run/index.d.ts.map +1 -0
- package/dist/run/index.js +16 -0
- package/dist/run/index.js.map +1 -0
- package/dist/run-lifecycle.d.ts +118 -0
- package/dist/run-lifecycle.d.ts.map +1 -0
- package/dist/run-lifecycle.js +451 -0
- package/dist/run-lifecycle.js.map +1 -0
- package/dist/terminal-session.d.ts +139 -0
- package/dist/terminal-session.d.ts.map +1 -0
- package/dist/terminal-session.js +316 -0
- package/dist/terminal-session.js.map +1 -0
- package/dist/tokens.d.ts +9 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +19 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tool-executor.d.ts +97 -0
- package/dist/tool-executor.d.ts.map +1 -0
- package/dist/tool-executor.js +198 -0
- package/dist/tool-executor.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module terminal-session
|
|
3
|
+
*
|
|
4
|
+
* Interactive-terminal session manager: the `@jini-ai/daemon`-side wiring for
|
|
5
|
+
* OD's `apps/daemon/src/terminals.ts` + `apps/daemon/src/routes/terminal.ts`
|
|
6
|
+
* (see `ADS-memory/reports/proposals/PROP-http-route-packs-terminal-pty-2026-07-21.md`
|
|
7
|
+
* for the full design discussion this module implements a specific decision
|
|
8
|
+
* from). The generic ring-buffer/coalescing/SSE-agnostic session engine
|
|
9
|
+
* already exists as `@jini-ai/platform`'s `terminal.ts` (ported 2026-07-18,
|
|
10
|
+
* before this task) — this module does **not** reimplement that. It adds
|
|
11
|
+
* exactly the three things that engine deliberately left to a caller:
|
|
12
|
+
*
|
|
13
|
+
* 1. **A real `node-pty`-backed `PtySpawn`.** `@jini-ai/platform` intentionally
|
|
14
|
+
* does not depend on `node-pty` (a native compiled addon); `@jini-ai/daemon`
|
|
15
|
+
* does (see this package's `package.json` and `source-map.md`'s dated
|
|
16
|
+
* section for why that dependency now lives here, not in `@jini-ai/http` or
|
|
17
|
+
* `@jini-ai/platform`). {@link loadRealSpawnPty} is the real, dynamically
|
|
18
|
+
* imported default the reference session manager below uses.
|
|
19
|
+
*
|
|
20
|
+
* 2. **Session-token gating.** The proposal's blocker was that a
|
|
21
|
+
* *session-scoped* dangerous capability (once a shell is spawned, every
|
|
22
|
+
* subsequent `stdin` write is arbitrary unaudited input for the
|
|
23
|
+
* session's lifetime) does not fit `ToolExecutor`'s call-scoped shape.
|
|
24
|
+
* The resolved design: `ToolExecutor.execute('terminal.create', ...)`
|
|
25
|
+
* (via {@link createTerminalToolRegistrations}) is the one full
|
|
26
|
+
* deny-by-default gated call — it authorizes *creating* a session.
|
|
27
|
+
* Every subsequent `write`/`resize`/`kill`/`attach` call against that
|
|
28
|
+
* session id is validated by a lighter, still-explicit check: this
|
|
29
|
+
* module records which `Principal` created each session at `create()`
|
|
30
|
+
* time, and every later call verifies the calling principal matches,
|
|
31
|
+
* denying (as a `'not-found'` result, never a distinguishable "forbidden"
|
|
32
|
+
* — see {@link checkOwnership}'s doc for why) otherwise. This is not
|
|
33
|
+
* "the session id is unguessable therefore secure" — it is a real,
|
|
34
|
+
* per-call identity check, independent of how hard the id is to guess.
|
|
35
|
+
*
|
|
36
|
+
* 3. **The kill/stdin/resize race fix.** OD's origin (and `@jini-ai/platform`'s
|
|
37
|
+
* faithful port of it) has no lock between a `kill` and a concurrent
|
|
38
|
+
* `write`/`resize` on the same session id: `kill()` only *requests* the
|
|
39
|
+
* real OS process die (sends `SIGTERM`) — the session's `status` does not
|
|
40
|
+
* flip to `'exited'` until the pty's own `onExit` callback fires,
|
|
41
|
+
* asynchronously, once the real process actually dies. A `write`/`resize`
|
|
42
|
+
* processed in that window still observes `status: 'running'` and still
|
|
43
|
+
* reaches the real pty. This module closes that window with its own
|
|
44
|
+
* immediate, lock-protected `killed` flag (see {@link runExclusive} and
|
|
45
|
+
* the `write`/`resize`/`kill` implementations below): once a `kill` call
|
|
46
|
+
* has run its own critical section, any `write`/`resize` queued behind it
|
|
47
|
+
* for the same session id sees `killed: true` and is rejected — it never
|
|
48
|
+
* depends on the real process having actually exited yet.
|
|
49
|
+
*/
|
|
50
|
+
import { createRequire } from 'node:module';
|
|
51
|
+
import { createTerminalService, ensureSpawnHelperExecutable, spawnHelperCandidatePaths, } from '@jini-ai/platform';
|
|
52
|
+
/** The `ToolRegistry` id `terminal.create` executes through — see module doc §2. */
|
|
53
|
+
export const TERMINAL_CREATE_TOOL_ID = 'terminal.create';
|
|
54
|
+
/**
|
|
55
|
+
* Deny-by-default `ToolPolicy` for `terminal.create`. Matching
|
|
56
|
+
* `db-ops.ts`'s `denyAllDaemonDbPolicy` / `@jini-ai/deploy`'s
|
|
57
|
+
* `denyAllDeployPublishPolicy` precedent: spawning an interactive shell is
|
|
58
|
+
* strictly more dangerous than any of those (it is not one bounded
|
|
59
|
+
* operation but an open-ended session), so a host must explicitly opt in
|
|
60
|
+
* with its own policy rather than getting a working shell spawner for free
|
|
61
|
+
* merely by registering the tool.
|
|
62
|
+
*/
|
|
63
|
+
export const denyAllTerminalCreatePolicy = {
|
|
64
|
+
authorize: () => 'deny',
|
|
65
|
+
};
|
|
66
|
+
const daemonRequire = createRequire(import.meta.url);
|
|
67
|
+
/** Adapts a real `node-pty` `IPty` to `@jini-ai/platform`'s narrower `PtyProcess` port — explicit per-member mapping rather than relying on structural assignability, matching this package's established "explicit adapter, no implicit coercion" convention (e.g. `agent-executor.ts`'s `toStringEnvRecord`). */
|
|
68
|
+
function toPtyProcess(pty) {
|
|
69
|
+
return {
|
|
70
|
+
onData: (cb) => {
|
|
71
|
+
pty.onData(cb);
|
|
72
|
+
},
|
|
73
|
+
onExit: (cb) => {
|
|
74
|
+
pty.onExit(cb);
|
|
75
|
+
},
|
|
76
|
+
write: (input) => pty.write(input),
|
|
77
|
+
resize: (cols, rows) => pty.resize(cols, rows),
|
|
78
|
+
kill: (signal) => pty.kill(signal),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The real, `node-pty`-backed {@link PtySpawn}. Dynamically imports
|
|
83
|
+
* `node-pty` (mirroring OD's origin `await import('node-pty')`, not a
|
|
84
|
+
* static top-level import) so a host process whose platform/environment
|
|
85
|
+
* lacks a usable `node-pty` addon can still boot — only an actual
|
|
86
|
+
* `terminal.create` call fails, cleanly, rather than crashing at
|
|
87
|
+
* module-eval time. Before the first spawn, repairs `node-pty`'s bundled
|
|
88
|
+
* `spawn-helper` binary's executable bit (pnpm unpacks it non-executable —
|
|
89
|
+
* see `@jini-ai/platform`'s `terminal.ts` module doc for the full story) via
|
|
90
|
+
* that package's own `spawnHelperCandidatePaths`/`ensureSpawnHelperExecutable`,
|
|
91
|
+
* but with the resolver anchored at **this** module
|
|
92
|
+
* (`createRequire(import.meta.url)` here, not `@jini-ai/platform`'s own
|
|
93
|
+
* default): `@jini-ai/platform` deliberately does not depend on `node-pty`, so
|
|
94
|
+
* its default `require.resolve('node-pty')` would not find the copy this
|
|
95
|
+
* package (`@jini-ai/daemon`) actually declares — this is exactly the
|
|
96
|
+
* resolver injection seam `spawnHelperCandidatePaths` was built for.
|
|
97
|
+
*/
|
|
98
|
+
export async function loadRealSpawnPty() {
|
|
99
|
+
ensureSpawnHelperExecutable(spawnHelperCandidatePaths({ resolve: (specifier) => daemonRequire.resolve(specifier) }));
|
|
100
|
+
const nodePty = await import('node-pty');
|
|
101
|
+
return (shell, args, options) => toPtyProcess(nodePty.spawn(shell, args, options));
|
|
102
|
+
}
|
|
103
|
+
function toSessionInfo(session, resourceRef) {
|
|
104
|
+
return {
|
|
105
|
+
id: session.id,
|
|
106
|
+
resourceRef,
|
|
107
|
+
cwd: session.cwd,
|
|
108
|
+
shell: session.shell,
|
|
109
|
+
cols: session.cols,
|
|
110
|
+
rows: session.rows,
|
|
111
|
+
status: session.status,
|
|
112
|
+
createdAt: session.createdAt,
|
|
113
|
+
updatedAt: session.updatedAt,
|
|
114
|
+
exitCode: session.exitCode,
|
|
115
|
+
signal: session.signal,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Serializes calls made for the same `id` through a per-id promise chain
|
|
120
|
+
* (matching this codebase's `oauth-tokens.ts` `withLock` precedent), so a
|
|
121
|
+
* `kill` and a concurrent `write`/`resize` against the same session never
|
|
122
|
+
* interleave — see module doc §3. Self-cleans `locks` once a chain becomes
|
|
123
|
+
* uncontended, so the map does not grow unbounded across a long-lived
|
|
124
|
+
* daemon process's many short-lived terminal sessions.
|
|
125
|
+
*/
|
|
126
|
+
async function runExclusive(locks, id, fn) {
|
|
127
|
+
const prior = locks.get(id) ?? Promise.resolve();
|
|
128
|
+
const task = prior.catch(() => undefined).then(fn);
|
|
129
|
+
locks.set(id, task);
|
|
130
|
+
try {
|
|
131
|
+
return await task;
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
if (locks.get(id) === task)
|
|
135
|
+
locks.delete(id);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Builds the reference `TerminalSessionManager`: an in-process wrapper
|
|
140
|
+
* around a `@jini-ai/platform` `TerminalService` adding session ownership,
|
|
141
|
+
* the kill/write/resize lock, and (via {@link createTerminalToolRegistrations})
|
|
142
|
+
* `ToolExecutor` gating for creation. See module doc for the full design.
|
|
143
|
+
*/
|
|
144
|
+
export function createTerminalSessionManager(options = {}) {
|
|
145
|
+
const terminalService = options.terminalService ??
|
|
146
|
+
createTerminalService({
|
|
147
|
+
loadSpawnPty: options.loadSpawnPty ?? loadRealSpawnPty,
|
|
148
|
+
...(options.maxEvents !== undefined ? { maxEvents: options.maxEvents } : {}),
|
|
149
|
+
...(options.maxBufferBytes !== undefined ? { maxBufferBytes: options.maxBufferBytes } : {}),
|
|
150
|
+
...(options.exitTailBytes !== undefined ? { exitTailBytes: options.exitTailBytes } : {}),
|
|
151
|
+
...(options.flushIntervalMs !== undefined ? { flushIntervalMs: options.flushIntervalMs } : {}),
|
|
152
|
+
...(options.flushThresholdBytes !== undefined ? { flushThresholdBytes: options.flushThresholdBytes } : {}),
|
|
153
|
+
...(options.ttlMs !== undefined ? { ttlMs: options.ttlMs } : {}),
|
|
154
|
+
...(options.shutdownGraceMs !== undefined ? { shutdownGraceMs: options.shutdownGraceMs } : {}),
|
|
155
|
+
});
|
|
156
|
+
const metadata = new Map();
|
|
157
|
+
const locks = new Map();
|
|
158
|
+
/**
|
|
159
|
+
* Looks up `id`, pruning this module's own metadata if the underlying
|
|
160
|
+
* `@jini-ai/platform` service has already reaped it (past its TTL) — bounds
|
|
161
|
+
* `metadata`'s growth to "until the next access attempt after expiry"
|
|
162
|
+
* rather than tracking every session forever.
|
|
163
|
+
*/
|
|
164
|
+
function checkOwnership(principal, id) {
|
|
165
|
+
const session = terminalService.get(id);
|
|
166
|
+
if (!session) {
|
|
167
|
+
metadata.delete(id);
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const meta = metadata.get(id);
|
|
171
|
+
// A session with no metadata (should not happen for anything created through this manager)
|
|
172
|
+
// or a mismatched principal is reported identically to "does not exist" — never a
|
|
173
|
+
// distinguishable "forbidden" — so this surface cannot be used to enumerate another
|
|
174
|
+
// principal's session ids. Matches OD's own `resolveSession`: a foreign session id was
|
|
175
|
+
// already a 404, not a 403.
|
|
176
|
+
if (!meta || meta.principalId !== principal.id)
|
|
177
|
+
return null;
|
|
178
|
+
return { session, meta };
|
|
179
|
+
}
|
|
180
|
+
async function create(principal, createOptions) {
|
|
181
|
+
const session = await terminalService.create({
|
|
182
|
+
cwd: createOptions.cwd,
|
|
183
|
+
...(createOptions.cols !== undefined ? { cols: createOptions.cols } : {}),
|
|
184
|
+
...(createOptions.rows !== undefined ? { rows: createOptions.rows } : {}),
|
|
185
|
+
...(createOptions.shell !== undefined ? { shell: createOptions.shell } : {}),
|
|
186
|
+
});
|
|
187
|
+
const resourceRef = createOptions.resourceRef ?? null;
|
|
188
|
+
metadata.set(session.id, { principalId: principal.id, resourceRef, killed: false });
|
|
189
|
+
return toSessionInfo(session, resourceRef);
|
|
190
|
+
}
|
|
191
|
+
function get(principal, id) {
|
|
192
|
+
const found = checkOwnership(principal, id);
|
|
193
|
+
return found ? { status: 'ok', session: toSessionInfo(found.session, found.meta.resourceRef) } : { status: 'not-found' };
|
|
194
|
+
}
|
|
195
|
+
function list(principal, filter = {}) {
|
|
196
|
+
const result = [];
|
|
197
|
+
for (const session of terminalService.list()) {
|
|
198
|
+
const meta = metadata.get(session.id);
|
|
199
|
+
if (!meta || meta.principalId !== principal.id)
|
|
200
|
+
continue;
|
|
201
|
+
if (filter.resourceRef !== undefined && meta.resourceRef !== filter.resourceRef)
|
|
202
|
+
continue;
|
|
203
|
+
result.push(toSessionInfo(session, meta.resourceRef));
|
|
204
|
+
}
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
/** Snapshots the current session (or null if it has meanwhile vanished) for an action result — shared by `write`/`resize`/`kill`. */
|
|
208
|
+
function currentSnapshot(id, resourceRef) {
|
|
209
|
+
const session = terminalService.get(id);
|
|
210
|
+
return session ? toSessionInfo(session, resourceRef) : null;
|
|
211
|
+
}
|
|
212
|
+
async function write(principal, id, input) {
|
|
213
|
+
const found = checkOwnership(principal, id);
|
|
214
|
+
if (!found)
|
|
215
|
+
return { status: 'not-found' };
|
|
216
|
+
return runExclusive(locks, id, () => {
|
|
217
|
+
// Re-read (not reuse `found.meta`) so a `kill` that ran its own critical section between
|
|
218
|
+
// this call's ownership check and lock acquisition is observed — see module doc §3.
|
|
219
|
+
const meta = metadata.get(id);
|
|
220
|
+
if (!meta || meta.killed) {
|
|
221
|
+
return { status: 'ok', ok: false, session: currentSnapshot(id, meta?.resourceRef ?? null) };
|
|
222
|
+
}
|
|
223
|
+
const ok = terminalService.write(id, input);
|
|
224
|
+
return { status: 'ok', ok, session: currentSnapshot(id, meta.resourceRef) };
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
async function resize(principal, id, cols, rows) {
|
|
228
|
+
const found = checkOwnership(principal, id);
|
|
229
|
+
if (!found)
|
|
230
|
+
return { status: 'not-found' };
|
|
231
|
+
return runExclusive(locks, id, () => {
|
|
232
|
+
const meta = metadata.get(id);
|
|
233
|
+
if (!meta || meta.killed) {
|
|
234
|
+
return { status: 'ok', ok: false, session: currentSnapshot(id, meta?.resourceRef ?? null) };
|
|
235
|
+
}
|
|
236
|
+
const ok = terminalService.resize(id, cols, rows);
|
|
237
|
+
return { status: 'ok', ok, session: currentSnapshot(id, meta.resourceRef) };
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async function kill(principal, id, signal) {
|
|
241
|
+
const found = checkOwnership(principal, id);
|
|
242
|
+
if (!found)
|
|
243
|
+
return { status: 'not-found' };
|
|
244
|
+
return runExclusive(locks, id, () => {
|
|
245
|
+
const meta = metadata.get(id);
|
|
246
|
+
// Set the flag before delegating, still inside the lock: any write()/resize() queued
|
|
247
|
+
// behind this call re-reads `meta.killed` once it acquires the lock (see those functions
|
|
248
|
+
// above), so it observes `true` regardless of whether the real OS process has exited yet.
|
|
249
|
+
if (meta)
|
|
250
|
+
meta.killed = true;
|
|
251
|
+
const ok = terminalService.kill(id, signal);
|
|
252
|
+
return { status: 'ok', ok, session: currentSnapshot(id, meta?.resourceRef ?? null) };
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function attach(principal, id, lastEventId, sink) {
|
|
256
|
+
const found = checkOwnership(principal, id);
|
|
257
|
+
if (!found)
|
|
258
|
+
return 'not-found';
|
|
259
|
+
return terminalService.attach(id, lastEventId, sink);
|
|
260
|
+
}
|
|
261
|
+
function detach(id, sink) {
|
|
262
|
+
terminalService.detach(id, sink);
|
|
263
|
+
}
|
|
264
|
+
async function shutdownActive(shutdownOptions) {
|
|
265
|
+
await terminalService.shutdownActive(shutdownOptions);
|
|
266
|
+
}
|
|
267
|
+
return { create, get, list, write, resize, kill, attach, detach, shutdownActive };
|
|
268
|
+
}
|
|
269
|
+
function isRecord(value) {
|
|
270
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Builds the `{descriptor, handler, policy}` triple a host registers
|
|
274
|
+
* against `@jini-ai/core`'s `ToolRegistry` so `terminal.create` becomes
|
|
275
|
+
* reachable only via `ToolExecutor.execute(principal, run,
|
|
276
|
+
* 'terminal.create', input)` — never by a route calling
|
|
277
|
+
* `manager.create()` directly, which would bypass authorization and the
|
|
278
|
+
* audit trail (matching `db-ops.ts`'s `createDaemonDbToolRegistrations`
|
|
279
|
+
* precedent).
|
|
280
|
+
*/
|
|
281
|
+
export function createTerminalToolRegistrations(options) {
|
|
282
|
+
const { manager, policy = denyAllTerminalCreatePolicy, requiresConfirmation, timeoutMs } = options;
|
|
283
|
+
const descriptorExtras = {
|
|
284
|
+
...(requiresConfirmation !== undefined ? { requiresConfirmation } : {}),
|
|
285
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
286
|
+
};
|
|
287
|
+
return {
|
|
288
|
+
create: {
|
|
289
|
+
descriptor: {
|
|
290
|
+
id: TERMINAL_CREATE_TOOL_ID,
|
|
291
|
+
description: 'Spawns an interactive shell session (a real PTY) rooted at a resolved working directory and returns its session id. Every subsequent write/resize/kill against that id is validated separately (see terminal-session.ts) rather than re-gated through this tool.',
|
|
292
|
+
...descriptorExtras,
|
|
293
|
+
},
|
|
294
|
+
policy,
|
|
295
|
+
handler: async (ctx) => {
|
|
296
|
+
const record = isRecord(ctx.input) ? ctx.input : {};
|
|
297
|
+
const cwd = record.cwd;
|
|
298
|
+
if (typeof cwd !== 'string' || cwd.length === 0) {
|
|
299
|
+
throw new Error('terminal.create: input.cwd must be a non-empty string');
|
|
300
|
+
}
|
|
301
|
+
const resourceRef = typeof record.resourceRef === 'string' ? record.resourceRef : null;
|
|
302
|
+
const cols = typeof record.cols === 'number' ? record.cols : undefined;
|
|
303
|
+
const rows = typeof record.rows === 'number' ? record.rows : undefined;
|
|
304
|
+
const shell = typeof record.shell === 'string' ? record.shell : null;
|
|
305
|
+
return manager.create(ctx.principal, {
|
|
306
|
+
resourceRef,
|
|
307
|
+
cwd,
|
|
308
|
+
...(cols !== undefined ? { cols } : {}),
|
|
309
|
+
...(rows !== undefined ? { rows } : {}),
|
|
310
|
+
shell,
|
|
311
|
+
});
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=terminal-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-session.js","sourceRoot":"","sources":["../src/terminal-session.ts"],"names":[],"mappings":"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"}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EventLog } from './event-log.js';
|
|
2
|
+
import type { RunLifecycle } from './run-lifecycle.js';
|
|
3
|
+
import type { ToolExecutor } from './tool-executor.js';
|
|
4
|
+
import type { AgentExecutor } from './agent-executor.js';
|
|
5
|
+
export declare const RunLifecycleToken: import("@jini-ai/core").Token<RunLifecycle, string>;
|
|
6
|
+
export declare const EventLogToken: import("@jini-ai/core").Token<EventLog, string>;
|
|
7
|
+
export declare const ToolExecutorToken: import("@jini-ai/core").Token<ToolExecutor, string>;
|
|
8
|
+
export declare const AgentExecutorToken: import("@jini-ai/core").Token<AgentExecutor, string>;
|
|
9
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,eAAO,MAAM,iBAAiB,qDAA2C,CAAC;AAC1E,eAAO,MAAM,aAAa,iDAAmC,CAAC;AAC9D,eAAO,MAAM,iBAAiB,qDAA2C,CAAC;AAC1E,eAAO,MAAM,kBAAkB,sDAA6C,CAAC"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@jini-ai/core` DI tokens for this package's kernel services, per
|
|
3
|
+
* extraction-plan §2.2 ("Kernel exports only kernel-service tokens... every
|
|
4
|
+
* other token lives in its owning feature package") — `RunLifecycle`,
|
|
5
|
+
* `EventLog`, and `ToolExecutor` are themselves kernel nouns (§2.1), so
|
|
6
|
+
* their tokens are defined here, alongside the services, rather than in
|
|
7
|
+
* `@jini-ai/core` itself. A pack composes against them the same way
|
|
8
|
+
* `packages/core/src/index.test.ts` demonstrates for its own example tokens.
|
|
9
|
+
*
|
|
10
|
+
* `ArtifactStoreToken` used to live here too — moved to `@jini-ai/artifacts`'s own
|
|
11
|
+
* `tokens.ts` on 2026-07-19 (see that file's doc comment) after the swarm-consensus
|
|
12
|
+
* architecture debate found it violated §2.1's "NO ... artifacts ... in the kernel" rule.
|
|
13
|
+
*/
|
|
14
|
+
import { token } from '@jini-ai/core';
|
|
15
|
+
export const RunLifecycleToken = token('jini.runLifecycle');
|
|
16
|
+
export const EventLogToken = token('jini.eventLog');
|
|
17
|
+
export const ToolExecutorToken = token('jini.toolExecutor');
|
|
18
|
+
export const AgentExecutorToken = token('jini.agentExecutor');
|
|
19
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAMtC,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAe,mBAAmB,CAAC,CAAC;AAC1E,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAW,eAAe,CAAC,CAAC;AAC9D,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAe,mBAAmB,CAAC,CAAC;AAC1E,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAgB,oBAAoB,CAAC,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { AuthorizationDecision, Principal, RunRef, ToolDescriptor, ToolRegistry } from '@jini-ai/core';
|
|
2
|
+
export type ConfirmationDecision = 'confirm' | 'deny';
|
|
3
|
+
export interface ToolAuthorizationRequest {
|
|
4
|
+
readonly principal: Principal;
|
|
5
|
+
readonly run: RunRef;
|
|
6
|
+
readonly tool: ToolDescriptor;
|
|
7
|
+
readonly input: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface ToolConfirmationRequest {
|
|
10
|
+
readonly executionId: string;
|
|
11
|
+
readonly principal: Principal;
|
|
12
|
+
readonly run: RunRef;
|
|
13
|
+
readonly tool: ToolDescriptor;
|
|
14
|
+
readonly input: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The transport-supplied seam for the two touchpoints a headless kernel
|
|
18
|
+
* cannot render itself. Both are optional: a tool whose `ToolPolicy`
|
|
19
|
+
* always resolves definitively needs no `onAuthorize`, and a tool with
|
|
20
|
+
* `requiresConfirmation` unset never triggers `onConfirm`.
|
|
21
|
+
*/
|
|
22
|
+
export interface ExecutionDelegate {
|
|
23
|
+
/**
|
|
24
|
+
* Consulted only when the tool's own `ToolPolicy.authorize` already
|
|
25
|
+
* returned `'allow'` — an additional transport-level veto (e.g. "does
|
|
26
|
+
* this session actually have an active grant for this tool"), not a
|
|
27
|
+
* replacement for the policy. Returning `'deny'` (or a Promise resolving
|
|
28
|
+
* to `'deny'`) overrides an `'allow'` from the policy; omitting
|
|
29
|
+
* `onAuthorize` entirely leaves the policy's decision as final.
|
|
30
|
+
*/
|
|
31
|
+
onAuthorize?(request: ToolAuthorizationRequest): AuthorizationDecision | Promise<AuthorizationDecision>;
|
|
32
|
+
/**
|
|
33
|
+
* Notified when a tool requiring confirmation is about to run. Return a
|
|
34
|
+
* decision (sync value or Promise) to supply it inline; return/resolve
|
|
35
|
+
* `undefined` — including simply omitting `onConfirm` — to signal "I'll
|
|
36
|
+
* call `resumeConfirmation` later" instead. Note the synchronous-`void`
|
|
37
|
+
* case is what actually parks the wait: an `async` implementation that
|
|
38
|
+
* resolves to `undefined` is treated as "the decision is `undefined`"
|
|
39
|
+
* (denied) rather than "pending" — an implementation that wants the
|
|
40
|
+
* resumable path should return a bare `undefined`, not a resolved
|
|
41
|
+
* Promise of one. See the module doc for why.
|
|
42
|
+
*/
|
|
43
|
+
onConfirm?(request: ToolConfirmationRequest): ConfirmationDecision | Promise<ConfirmationDecision> | void;
|
|
44
|
+
}
|
|
45
|
+
export type ToolExecutionPhase = 'requested' | 'authorized' | 'denied' | 'confirmed' | 'confirmation-denied' | 'started' | 'completed' | 'failed' | 'timed-out' | 'cancelled';
|
|
46
|
+
export interface ToolExecutionAuditEvent {
|
|
47
|
+
readonly phase: ToolExecutionPhase;
|
|
48
|
+
readonly at: number;
|
|
49
|
+
readonly detail?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ToolExecutionAuditRecord {
|
|
52
|
+
readonly executionId: string;
|
|
53
|
+
readonly toolId: string;
|
|
54
|
+
readonly principalId: string;
|
|
55
|
+
readonly runId: string;
|
|
56
|
+
readonly events: readonly ToolExecutionAuditEvent[];
|
|
57
|
+
}
|
|
58
|
+
export type ToolExecutionStatus = 'completed' | 'denied' | 'confirmation-denied' | 'timed-out' | 'cancelled' | 'failed';
|
|
59
|
+
export interface ToolExecutionResult {
|
|
60
|
+
readonly executionId: string;
|
|
61
|
+
readonly status: ToolExecutionStatus;
|
|
62
|
+
readonly output?: unknown;
|
|
63
|
+
readonly truncated?: boolean;
|
|
64
|
+
readonly error?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ToolExecutor {
|
|
67
|
+
/** @throws If `toolId` isn't registered — a routing/programming error, distinct from the denial/confirmation-denial states `ToolExecutionResult.status` covers. */
|
|
68
|
+
execute(principal: Principal, run: RunRef, toolId: string, input: unknown, signal?: AbortSignal): Promise<ToolExecutionResult>;
|
|
69
|
+
/** @throws If `executionId` has no confirmation currently pending (already resumed, never required one, or unknown). */
|
|
70
|
+
resumeConfirmation(executionId: string, decision: ConfirmationDecision): void;
|
|
71
|
+
/** Aborts an in-flight handler, or resolves a still-pending confirmation as denied. No-op if `executionId` is already terminal or unknown. */
|
|
72
|
+
cancel(executionId: string): void;
|
|
73
|
+
getAuditRecord(executionId: string): ToolExecutionAuditRecord | null;
|
|
74
|
+
}
|
|
75
|
+
export interface CreateToolExecutorOptions {
|
|
76
|
+
readonly registry: ToolRegistry;
|
|
77
|
+
readonly delegate?: ExecutionDelegate;
|
|
78
|
+
/** Injectable clock for audit timestamps — defaults to `Date.now`. Test-only hook. */
|
|
79
|
+
readonly now?: () => number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Creates the `ToolExecutor` reference implementation: an in-process,
|
|
83
|
+
* in-memory gate over a `ToolRegistry`. No persistence — a real host that
|
|
84
|
+
* needs audit records to survive a restart layers a durable store behind
|
|
85
|
+
* `getAuditRecord`/an append hook later; out of this task's scope (see
|
|
86
|
+
* `source-map.md`).
|
|
87
|
+
*
|
|
88
|
+
* @param options.registry - The `ToolRegistry` to resolve `{descriptor,
|
|
89
|
+
* handler, policy}` triples from, via `@jini-ai/core/internal`'s
|
|
90
|
+
* `getToolRegistration` — the one and only caller of that internal export.
|
|
91
|
+
* @param options.delegate - Transport-supplied authorize/confirm UI seam;
|
|
92
|
+
* omit for a headless caller whose tools never need interactive gating.
|
|
93
|
+
* @complexity `execute` is O(1) plus the handler's own cost; `resumeConfirmation`/`cancel`/`getAuditRecord` are O(1) map lookups.
|
|
94
|
+
* @overallScore 100/100
|
|
95
|
+
*/
|
|
96
|
+
export declare function createToolExecutor(options: CreateToolExecutorOptions): ToolExecutor;
|
|
97
|
+
//# sourceMappingURL=tool-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-executor.d.ts","sourceRoot":"","sources":["../src/tool-executor.ts"],"names":[],"mappings":"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"}
|