@jini-ai/daemon 0.3.8 → 0.5.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 +124 -0
- package/README.md +50 -8
- package/dist/agent-executor/contracts.d.ts +736 -0
- package/dist/agent-executor/contracts.js +28 -0
- package/dist/agent-executor/event-translation.d.ts +87 -0
- package/dist/agent-executor/event-translation.js +230 -0
- package/dist/agent-executor/events.d.ts +190 -0
- package/dist/agent-executor/events.js +848 -0
- package/dist/agent-executor/handoff.d.ts +18 -0
- package/dist/agent-executor/handoff.js +16 -0
- package/dist/agent-executor/index.d.ts +98 -0
- package/dist/agent-executor/index.js +98 -0
- package/dist/agent-executor/launch.d.ts +375 -0
- package/dist/agent-executor/launch.js +1001 -0
- package/dist/agent-executor/mcp-bridge.d.ts +338 -0
- package/dist/agent-executor/mcp-bridge.js +835 -0
- package/dist/agent-executor/values.d.ts +5 -0
- package/dist/agent-executor/values.js +13 -0
- package/dist/agent-executor.d.ts +375 -176
- package/dist/agent-executor.js +388 -335
- package/dist/close-status.d.ts +11 -8
- package/dist/close-status.js +31 -19
- package/dist/continuation/continuation-transport.d.ts +3 -1
- package/dist/continuation/continuation-transport.js +1 -1
- package/dist/continuation/journal.d.ts +12 -5
- package/dist/continuation/journal.js +4 -4
- package/dist/continuation/run-scoped-context-store.d.ts +13 -4
- package/dist/continuation/run-scoped-context-store.js +6 -6
- package/dist/continuation/run-start-handler.d.ts +1 -1
- package/dist/continuation/run-start-handler.js +3 -16
- package/dist/delegated-tool-bridge.d.ts +7 -3
- package/dist/delegated-tool-bridge.js +32 -31
- package/dist/event-log.d.ts +7 -5
- package/dist/event-log.js +12 -11
- package/dist/frontend-capability-tools.d.ts +1 -1
- package/dist/frontend-capability-tools.js +3 -2
- package/dist/frontend-session-registry.d.ts +32 -9
- package/dist/frontend-session-registry.js +12 -11
- package/dist/http/active-context.d.ts +105 -0
- package/dist/http/active-context.js +67 -0
- package/dist/http/agents.d.ts +68 -0
- package/dist/http/agents.js +12 -0
- package/dist/http/attachments.d.ts +618 -0
- package/dist/http/attachments.js +1034 -0
- package/dist/http/cancel-owned-runs.d.ts +49 -0
- package/dist/http/cancel-owned-runs.js +35 -0
- package/dist/http/component-catalog.d.ts +84 -0
- package/dist/http/component-catalog.js +64 -0
- package/dist/http/connectors.d.ts +209 -0
- package/dist/http/connectors.js +409 -0
- package/dist/http/daemon-status.d.ts +76 -0
- package/dist/http/daemon-status.js +55 -0
- package/dist/http/db-ops.d.ts +123 -0
- package/dist/http/db-ops.js +189 -0
- package/dist/http/delegated-tools.d.ts +113 -0
- package/dist/http/delegated-tools.js +230 -0
- package/dist/http/frontend-control.d.ts +136 -0
- package/dist/http/frontend-control.js +79 -0
- package/dist/http/frontend-sessions.d.ts +110 -0
- package/dist/http/frontend-sessions.js +180 -0
- package/dist/http/health.d.ts +95 -0
- package/dist/http/health.js +40 -0
- package/dist/http/host-tools.d.ts +214 -0
- package/dist/http/host-tools.js +329 -0
- package/dist/http/index.d.ts +52 -0
- package/dist/http/index.js +27 -0
- package/dist/http/media.d.ts +146 -0
- package/dist/http/media.js +346 -0
- package/dist/http/memory.d.ts +265 -0
- package/dist/http/memory.js +328 -0
- package/dist/http/model-proxy.d.ts +49 -0
- package/dist/http/model-proxy.js +642 -0
- package/dist/http/remote-run-events.d.ts +70 -0
- package/dist/http/remote-run-events.js +224 -0
- package/dist/http/research.d.ts +48 -0
- package/dist/http/research.js +315 -0
- package/dist/http/route-manifest.d.ts +35 -0
- package/dist/http/route-manifest.js +103 -0
- package/dist/http/routines.d.ts +120 -0
- package/dist/http/routines.js +298 -0
- package/dist/http/run-credentials.d.ts +73 -0
- package/dist/http/run-credentials.js +71 -0
- package/dist/http/runs.d.ts +91 -0
- package/dist/http/runs.js +221 -0
- package/dist/http/terminals.d.ts +138 -0
- package/dist/http/terminals.js +325 -0
- package/dist/http/tool-catalog.d.ts +78 -0
- package/dist/http/tool-catalog.js +66 -0
- package/dist/http/workspace-root.d.ts +77 -0
- package/dist/http/workspace-root.js +63 -0
- package/dist/http/xai.d.ts +90 -0
- package/dist/http/xai.js +655 -0
- package/dist/image-prompt-delivery.d.ts +13 -3
- package/dist/image-prompt-delivery.js +5 -5
- package/dist/index.d.ts +556 -4
- package/dist/index.js +556 -4
- package/dist/legacy-data-migration.d.ts +59 -8
- package/dist/legacy-data-migration.js +38 -33
- package/dist/read-only-tools.d.ts +130 -0
- package/dist/read-only-tools.js +115 -0
- package/dist/remote-tool-bridge.d.ts +11 -5
- package/dist/remote-tool-bridge.js +15 -15
- package/dist/routines/index.d.ts +1 -1
- package/dist/routines/index.js +1 -1
- package/dist/routines/routine-store.d.ts +34 -12
- package/dist/routines/routine-store.js +21 -20
- package/dist/routines/schedule.d.ts +48 -10
- package/dist/routines/schedule.js +21 -21
- package/dist/routines/scheduler.d.ts +32 -11
- package/dist/routines/scheduler.js +61 -57
- package/dist/routines/types.d.ts +10 -3
- package/dist/run/core/result.d.ts +6 -2
- package/dist/run/core/result.js +3 -3
- package/dist/run/core/retry.d.ts +17 -4
- package/dist/run/core/retry.js +7 -11
- package/dist/run/diagnostics/diagnostics.d.ts +14 -4
- package/dist/run/diagnostics/diagnostics.js +7 -7
- package/dist/run/index.d.ts +1 -1
- package/dist/run/index.js +1 -1
- package/dist/run-credentials.d.ts +138 -0
- package/dist/run-credentials.js +152 -0
- package/dist/run-lifecycle.d.ts +92 -33
- package/dist/run-lifecycle.js +81 -74
- package/dist/scheduler.d.ts +8 -0
- package/dist/scheduler.js +2 -0
- package/dist/session-coordination.d.ts +115 -0
- package/dist/session-coordination.js +111 -0
- package/dist/store/agent-sessions/errors.d.ts +8 -0
- package/dist/store/agent-sessions/errors.js +10 -0
- package/dist/store/agent-sessions/index.d.ts +12 -0
- package/dist/store/agent-sessions/index.js +19 -0
- package/dist/store/agent-sessions/pglite.d.ts +12 -0
- package/dist/store/agent-sessions/pglite.js +7 -0
- package/dist/store/agent-sessions/ports.d.ts +24 -0
- package/dist/store/agent-sessions/ports.js +2 -0
- package/dist/store/agent-sessions/postgres.d.ts +12 -0
- package/dist/store/agent-sessions/postgres.js +7 -0
- package/dist/store/agent-sessions/schema.d.ts +3 -0
- package/dist/store/agent-sessions/schema.js +23 -0
- package/dist/store/agent-sessions/sql.d.ts +35 -0
- package/dist/store/agent-sessions/sql.js +59 -0
- package/dist/store/agent-sessions/sqlite.d.ts +117 -0
- package/dist/store/agent-sessions/sqlite.js +128 -0
- package/dist/store/event-log/sqlite.d.ts +75 -0
- package/dist/store/event-log/sqlite.js +230 -0
- package/dist/surface-exchanges.d.ts +97 -0
- package/dist/surface-exchanges.js +153 -0
- package/dist/terminal-session.d.ts +43 -11
- package/dist/terminal-session.js +22 -20
- package/dist/tokens.js +4 -4
- package/dist/tool-audit.d.ts +114 -0
- package/dist/tool-audit.js +95 -0
- package/dist/tool-executor.d.ts +22 -7
- package/dist/tool-executor.js +16 -16
- package/dist/tool-result-media.d.ts +3 -1
- package/dist/tool-result-media.js +1 -1
- package/dist/tool-result-surfaces.d.ts +3 -1
- package/dist/tool-result-surfaces.js +1 -1
- package/package.json +127 -9
|
@@ -40,7 +40,7 @@ export interface FrontendSessionHandle {
|
|
|
40
40
|
* Detaches the surface. Every invocation still awaiting it is rejected rather than left pending,
|
|
41
41
|
* so a closed tab surfaces as a failed tool call instead of a run that never finishes.
|
|
42
42
|
*/
|
|
43
|
-
detach(): void;
|
|
43
|
+
detach(_args: Record<string, never>): void;
|
|
44
44
|
}
|
|
45
45
|
export interface FrontendSessionRegistry {
|
|
46
46
|
/**
|
|
@@ -49,7 +49,10 @@ export interface FrontendSessionRegistry {
|
|
|
49
49
|
*
|
|
50
50
|
* @throws If `sessionId` is already attached.
|
|
51
51
|
*/
|
|
52
|
-
attach(
|
|
52
|
+
attach(args: {
|
|
53
|
+
readonly descriptor: FrontendSessionDescriptor;
|
|
54
|
+
readonly deliver: (invocation: FrontendInvocation) => void;
|
|
55
|
+
}): FrontendSessionHandle;
|
|
53
56
|
/**
|
|
54
57
|
* Associates a started run with the surface that originated it, so capability calls made by that
|
|
55
58
|
* run's agent reach that surface. Re-binding the same run to the same session is a no-op; binding
|
|
@@ -61,7 +64,10 @@ export interface FrontendSessionRegistry {
|
|
|
61
64
|
* @throws If `sessionId` is not attached. Binding a run to a surface that is already gone would
|
|
62
65
|
* produce calls that hang until their tool timeout instead of failing immediately.
|
|
63
66
|
*/
|
|
64
|
-
bindRun(
|
|
67
|
+
bindRun(args: {
|
|
68
|
+
readonly runId: string;
|
|
69
|
+
readonly sessionId: string;
|
|
70
|
+
}): () => void;
|
|
65
71
|
/**
|
|
66
72
|
* Binds a run to whichever surface holds `bindToken` — the wire-safe form of {@link bindRun}.
|
|
67
73
|
*
|
|
@@ -77,29 +83,46 @@ export interface FrontendSessionRegistry {
|
|
|
77
83
|
* distinguish "never existed" from "detached" — telling a caller which one it guessed is how a
|
|
78
84
|
* probe learns whether a token is worth retrying.
|
|
79
85
|
*/
|
|
80
|
-
bindRunByToken(
|
|
86
|
+
bindRunByToken(args: {
|
|
87
|
+
readonly runId: string;
|
|
88
|
+
readonly bindToken: string;
|
|
89
|
+
}): () => void;
|
|
81
90
|
/**
|
|
82
91
|
* Routes one capability call to the surface bound to `runId` and resolves with its output.
|
|
83
92
|
*
|
|
84
93
|
* @throws If no surface is bound to `runId`, if the bound surface does not claim `capabilityId`,
|
|
85
94
|
* if delivery fails, if the surface answers with a refusal, or if `signal` aborts first.
|
|
86
95
|
*/
|
|
87
|
-
invoke(
|
|
96
|
+
invoke(args: {
|
|
97
|
+
readonly runId: string;
|
|
98
|
+
readonly capabilityId: string;
|
|
99
|
+
readonly input: Record<string, unknown>;
|
|
100
|
+
}, optionalArgs?: {
|
|
101
|
+
readonly signal?: AbortSignal;
|
|
102
|
+
}): Promise<unknown>;
|
|
88
103
|
/**
|
|
89
104
|
* Settles a pending invocation with the surface's answer.
|
|
90
105
|
*
|
|
91
106
|
* @returns `true` if this call settled it; `false` if it was already settled, unknown, or belongs
|
|
92
107
|
* to a different session — the duplicate-answer case, which is a no-op by design.
|
|
93
108
|
*/
|
|
94
|
-
settle(
|
|
109
|
+
settle(args: {
|
|
110
|
+
readonly sessionId: string;
|
|
111
|
+
readonly invocationId: string;
|
|
112
|
+
readonly outcome: FrontendOutcome;
|
|
113
|
+
}): boolean;
|
|
95
114
|
/**
|
|
96
115
|
* Capability ids reachable for `runId` right now — empty when nothing is bound. A caller uses
|
|
97
116
|
* this to advertise only what can actually be served, so an agent is never offered a capability
|
|
98
117
|
* that would fail the moment it tried.
|
|
99
118
|
*/
|
|
100
|
-
capabilitiesFor(
|
|
119
|
+
capabilitiesFor(args: {
|
|
120
|
+
readonly runId: string;
|
|
121
|
+
}): readonly string[];
|
|
101
122
|
/** The surface bound to `runId`, or `undefined` when there is none. */
|
|
102
|
-
sessionFor(
|
|
123
|
+
sessionFor(args: {
|
|
124
|
+
readonly runId: string;
|
|
125
|
+
}): FrontendSessionDescriptor | undefined;
|
|
103
126
|
}
|
|
104
127
|
export interface CreateFrontendSessionRegistryOptions {
|
|
105
128
|
/** Injectable id source for invocation ids — defaults to `randomUUID`. Test-only hook. */
|
|
@@ -123,5 +146,5 @@ export interface CreateFrontendSessionRegistryOptions {
|
|
|
123
146
|
* @complexity Every operation is O(1) except `detach`, which is O(p + b) in that surface\'s pending
|
|
124
147
|
* invocations and bound runs.
|
|
125
148
|
*/
|
|
126
|
-
export declare function createFrontendSessionRegistry(
|
|
149
|
+
export declare function createFrontendSessionRegistry(requiredArgs: Record<string, never>, optionalArgs?: Pick<CreateFrontendSessionRegistryOptions, "newInvocationId" | "newBindToken">): FrontendSessionRegistry;
|
|
127
150
|
//# sourceMappingURL=frontend-session-registry.d.ts.map
|
|
@@ -56,7 +56,8 @@ import { randomUUID } from 'node:crypto';
|
|
|
56
56
|
* @complexity Every operation is O(1) except `detach`, which is O(p + b) in that surface\'s pending
|
|
57
57
|
* invocations and bound runs.
|
|
58
58
|
*/
|
|
59
|
-
export function createFrontendSessionRegistry(
|
|
59
|
+
export function createFrontendSessionRegistry(requiredArgs, optionalArgs = {}) {
|
|
60
|
+
const options = { ...requiredArgs, ...optionalArgs };
|
|
60
61
|
const newInvocationId = options.newInvocationId ?? randomUUID;
|
|
61
62
|
const newBindToken = options.newBindToken ?? randomUUID;
|
|
62
63
|
const sessions = new Map();
|
|
@@ -105,7 +106,7 @@ export function createFrontendSessionRegistry(options = {}) {
|
|
|
105
106
|
}
|
|
106
107
|
return session;
|
|
107
108
|
}
|
|
108
|
-
function attach(descriptor, deliver) {
|
|
109
|
+
function attach({ descriptor, deliver }) {
|
|
109
110
|
if (sessions.has(descriptor.sessionId)) {
|
|
110
111
|
throw new Error(`FrontendSessionRegistry: session "${descriptor.sessionId}" is already attached`);
|
|
111
112
|
}
|
|
@@ -116,7 +117,7 @@ export function createFrontendSessionRegistry(options = {}) {
|
|
|
116
117
|
return {
|
|
117
118
|
sessionId: descriptor.sessionId,
|
|
118
119
|
bindToken,
|
|
119
|
-
detach() {
|
|
120
|
+
detach(_args) {
|
|
120
121
|
// Only tear down state this attachment still owns. A stale handle — one whose attachment
|
|
121
122
|
// already detached and whose session id has since been re-attached — must not remove the
|
|
122
123
|
// replacement or its bindings; the replacement's own token/pending state is not this
|
|
@@ -138,7 +139,7 @@ export function createFrontendSessionRegistry(options = {}) {
|
|
|
138
139
|
},
|
|
139
140
|
};
|
|
140
141
|
}
|
|
141
|
-
function bindRun(runId, sessionId) {
|
|
142
|
+
function bindRun({ runId, sessionId }) {
|
|
142
143
|
const session = sessions.get(sessionId);
|
|
143
144
|
if (session === undefined) {
|
|
144
145
|
throw new Error(`FrontendSessionRegistry: cannot bind run "${runId}" to unattached session "${sessionId}"`);
|
|
@@ -152,7 +153,7 @@ export function createFrontendSessionRegistry(options = {}) {
|
|
|
152
153
|
runBindings.delete(runId);
|
|
153
154
|
};
|
|
154
155
|
}
|
|
155
|
-
function bindRunByToken(runId, bindToken) {
|
|
156
|
+
function bindRunByToken({ runId, bindToken }) {
|
|
156
157
|
const sessionId = bindTokens.get(bindToken);
|
|
157
158
|
// One message for both "no such token" and "the surface holding it detached". Distinguishing
|
|
158
159
|
// them tells a caller whether a guess was close, which is the feedback a probe needs and the
|
|
@@ -160,13 +161,13 @@ export function createFrontendSessionRegistry(options = {}) {
|
|
|
160
161
|
if (sessionId === undefined) {
|
|
161
162
|
throw new Error(`FrontendSessionRegistry: cannot bind run "${runId}" — unknown or expired bind token`);
|
|
162
163
|
}
|
|
163
|
-
return bindRun(runId, sessionId);
|
|
164
|
+
return bindRun({ runId: runId, sessionId: sessionId });
|
|
164
165
|
}
|
|
165
166
|
// `async` so that a routing refusal from `resolveTarget` surfaces as a rejected promise like
|
|
166
167
|
// every other failure here. Throwing synchronously out of a Promise-returning function would
|
|
167
168
|
// make `invoke(...).catch(...)` an uncaught exception for exactly the fail-closed cases this
|
|
168
169
|
// registry exists to report.
|
|
169
|
-
async function invoke(runId, capabilityId, input, signal) {
|
|
170
|
+
async function invoke({ runId, capabilityId, input }, { signal } = {}) {
|
|
170
171
|
const target = resolveTarget(runId, capabilityId);
|
|
171
172
|
const invocationId = newInvocationId();
|
|
172
173
|
return new Promise((resolve, reject) => {
|
|
@@ -193,7 +194,7 @@ export function createFrontendSessionRegistry(options = {}) {
|
|
|
193
194
|
}
|
|
194
195
|
});
|
|
195
196
|
}
|
|
196
|
-
function settle(sessionId, invocationId, outcome) {
|
|
197
|
+
function settle({ sessionId, invocationId, outcome }) {
|
|
197
198
|
const session = sessions.get(sessionId);
|
|
198
199
|
const entry = session?.pending.get(invocationId);
|
|
199
200
|
if (session === undefined || entry === undefined)
|
|
@@ -206,11 +207,11 @@ export function createFrontendSessionRegistry(options = {}) {
|
|
|
206
207
|
entry.reject(new Error(outcome.message));
|
|
207
208
|
return true;
|
|
208
209
|
}
|
|
209
|
-
function sessionFor(runId) {
|
|
210
|
+
function sessionFor({ runId }) {
|
|
210
211
|
return runBindings.get(runId)?.descriptor;
|
|
211
212
|
}
|
|
212
|
-
function capabilitiesFor(runId) {
|
|
213
|
-
return sessionFor(runId)?.capabilities ?? [];
|
|
213
|
+
function capabilitiesFor({ runId }) {
|
|
214
|
+
return sessionFor({ runId: runId })?.capabilities ?? [];
|
|
214
215
|
}
|
|
215
216
|
return { attach, bindRun, bindRunByToken, invoke, settle, capabilitiesFor, sessionFor };
|
|
216
217
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module active-context
|
|
3
|
+
*
|
|
4
|
+
* Generic "what resource is the caller currently focused on?" channel: an
|
|
5
|
+
* in-memory, TTL-scoped pointer a client POSTs to record its current focus
|
|
6
|
+
* and GETs to read back (resolved against a real display name via an
|
|
7
|
+
* injected lookup). Ported from the routes-classification table's
|
|
8
|
+
* `active-context.ts` MIXED row (see `archived provenance ledger`) — the origin's only
|
|
9
|
+
* real coupling was `handleGetActive` calling into an OD project store to
|
|
10
|
+
* resolve a display name; the TTL store and both handlers were already
|
|
11
|
+
* product-neutral, and all handlers are synchronous (no async risk).
|
|
12
|
+
*
|
|
13
|
+
* The origin's `projectId`/`fileName` fields are generalized here to a
|
|
14
|
+
* single opaque `resourceRef` (was `projectId`) plus an optional `detail`
|
|
15
|
+
* string (was `fileName` — a sub-locator within that resource), matching
|
|
16
|
+
* the source-map's own "renaming to a generic resource ref" note. The
|
|
17
|
+
* origin's `deps.getProject` lookup becomes an injected `resolveResource`
|
|
18
|
+
* dependency, matching this branch's DI convention (see
|
|
19
|
+
* `daemon-status.ts`/`host-tools.ts`: real collaborators supplied by the
|
|
20
|
+
* caller rather than read from a global store).
|
|
21
|
+
*/
|
|
22
|
+
import type { Express } from 'express';
|
|
23
|
+
import { type AdapterContext } from '@jini-ai/http-kit';
|
|
24
|
+
/** How long a recorded focus stays valid before a GET treats it as stale and clears it. */
|
|
25
|
+
export declare const ACTIVE_CONTEXT_TTL_MS: number;
|
|
26
|
+
interface ActiveContext {
|
|
27
|
+
resourceRef: string;
|
|
28
|
+
detail: string | null;
|
|
29
|
+
ts: number;
|
|
30
|
+
}
|
|
31
|
+
interface ActiveContextStore {
|
|
32
|
+
current: ActiveContext | null;
|
|
33
|
+
}
|
|
34
|
+
type SetActiveInput = {
|
|
35
|
+
kind: 'clear';
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'set';
|
|
38
|
+
resourceRef: string;
|
|
39
|
+
detail: string | null;
|
|
40
|
+
};
|
|
41
|
+
type SetActiveOutput = {
|
|
42
|
+
active: false;
|
|
43
|
+
} | {
|
|
44
|
+
active: true;
|
|
45
|
+
resourceRef: string;
|
|
46
|
+
detail: string | null;
|
|
47
|
+
ts: number;
|
|
48
|
+
};
|
|
49
|
+
type GetActiveOutput = {
|
|
50
|
+
active: false;
|
|
51
|
+
} | {
|
|
52
|
+
active: true;
|
|
53
|
+
resourceRef: string;
|
|
54
|
+
resourceName: string | null;
|
|
55
|
+
detail: string | null;
|
|
56
|
+
ts: number;
|
|
57
|
+
ageMs: number;
|
|
58
|
+
};
|
|
59
|
+
/** A resolved resource's display name. `resolveResource` itself may return `null`/`undefined` (not this type) when the caller has no entry at all for a given resource ref (e.g. unknown or deleted). */
|
|
60
|
+
export interface ActiveContextResource {
|
|
61
|
+
readonly name?: string | null;
|
|
62
|
+
}
|
|
63
|
+
/** What a caller must supply to mount the active-context routes. */
|
|
64
|
+
export interface ActiveContextDeps {
|
|
65
|
+
/**
|
|
66
|
+
* Resolves a display name for a resource ref (was OD's project store's
|
|
67
|
+
* `getProject`). Injected so this module has no dependency on any
|
|
68
|
+
* particular resource store; may return `null`/`undefined` if the ref is
|
|
69
|
+
* unknown.
|
|
70
|
+
*/
|
|
71
|
+
resolveResource: ({ resourceRef }: {
|
|
72
|
+
resourceRef: string;
|
|
73
|
+
}) => ActiveContextResource | null | undefined;
|
|
74
|
+
/** Returns the current time in epoch ms. Defaults to `Date.now` when omitted. */
|
|
75
|
+
now?: () => number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Internal deps threaded to both handlers: the caller-supplied
|
|
79
|
+
* {@link ActiveContextDeps} plus the shared in-memory store built once per
|
|
80
|
+
* `registerActiveContextRoutes` call, so the two routes see the same state.
|
|
81
|
+
*/
|
|
82
|
+
interface ActiveContextRouteDeps {
|
|
83
|
+
store: ActiveContextStore;
|
|
84
|
+
resolveResource: ActiveContextDeps['resolveResource'];
|
|
85
|
+
now: () => number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* `POST /api/active` — records (or, with `{active: false}`, clears) the
|
|
89
|
+
* caller's current resource focus.
|
|
90
|
+
*/
|
|
91
|
+
export declare const setActiveRoute: import("@jini-ai/http-kit").JsonRouteSpec<SetActiveInput, SetActiveOutput, ActiveContextRouteDeps>;
|
|
92
|
+
/**
|
|
93
|
+
* `GET /api/active` — reads back the current resource focus, resolving a
|
|
94
|
+
* display name for it, or `{active: false}` if nothing is set or the
|
|
95
|
+
* recorded focus has aged past {@link ACTIVE_CONTEXT_TTL_MS}.
|
|
96
|
+
*/
|
|
97
|
+
export declare const getActiveRoute: import("@jini-ai/http-kit").JsonRouteSpec<void, GetActiveOutput, ActiveContextRouteDeps>;
|
|
98
|
+
/** Mounts both active-context routes on `app`, sharing one in-memory store between them. A pack's `http(app, services)` calls this directly. */
|
|
99
|
+
export declare function registerActiveContextRoutes({ app, deps, adapter }: {
|
|
100
|
+
readonly app: Express;
|
|
101
|
+
readonly deps: ActiveContextDeps;
|
|
102
|
+
readonly adapter: AdapterContext;
|
|
103
|
+
}, _optional?: Record<string, never>): void;
|
|
104
|
+
export {};
|
|
105
|
+
//# sourceMappingURL=active-context.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createApiError } from '@jini-ai/protocol';
|
|
2
|
+
import { defineJsonRoute, mountJsonRoute } from '@jini-ai/http-kit';
|
|
3
|
+
import { err, ok } from '@jini-ai/http-kit';
|
|
4
|
+
/** How long a recorded focus stays valid before a GET treats it as stale and clears it. */
|
|
5
|
+
export const ACTIVE_CONTEXT_TTL_MS = 5 * 60 * 1000;
|
|
6
|
+
function parseSetActive(raw) {
|
|
7
|
+
const body = (raw.body ?? {});
|
|
8
|
+
if (body.active === false) {
|
|
9
|
+
return ok({ value: { kind: 'clear' } });
|
|
10
|
+
}
|
|
11
|
+
const resourceRef = typeof body.resourceRef === 'string' ? body.resourceRef : '';
|
|
12
|
+
if (!resourceRef) {
|
|
13
|
+
return err({ error: createApiError({ code: 'BAD_REQUEST', message: 'resourceRef is required' }) });
|
|
14
|
+
}
|
|
15
|
+
const detail = typeof body.detail === 'string' && body.detail.length > 0 ? body.detail : null;
|
|
16
|
+
return ok({ value: { kind: 'set', resourceRef, detail } });
|
|
17
|
+
}
|
|
18
|
+
function handleSetActive({ input, deps }) {
|
|
19
|
+
if (input.kind === 'clear') {
|
|
20
|
+
deps.store.current = null;
|
|
21
|
+
return ok({ value: { active: false } });
|
|
22
|
+
}
|
|
23
|
+
const next = { resourceRef: input.resourceRef, detail: input.detail, ts: deps.now() };
|
|
24
|
+
deps.store.current = next;
|
|
25
|
+
return ok({ value: { active: true, ...next } });
|
|
26
|
+
}
|
|
27
|
+
function handleGetActive({ input: _input, deps }) {
|
|
28
|
+
const current = deps.store.current;
|
|
29
|
+
if (!current || deps.now() - current.ts > ACTIVE_CONTEXT_TTL_MS) {
|
|
30
|
+
deps.store.current = null;
|
|
31
|
+
return ok({ value: { active: false } });
|
|
32
|
+
}
|
|
33
|
+
const resource = deps.resolveResource({ resourceRef: current.resourceRef });
|
|
34
|
+
return ok({
|
|
35
|
+
value: {
|
|
36
|
+
active: true,
|
|
37
|
+
resourceRef: current.resourceRef,
|
|
38
|
+
resourceName: resource?.name ?? null,
|
|
39
|
+
detail: current.detail,
|
|
40
|
+
ts: current.ts,
|
|
41
|
+
ageMs: deps.now() - current.ts,
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `POST /api/active` — records (or, with `{active: false}`, clears) the
|
|
47
|
+
* caller's current resource focus.
|
|
48
|
+
*/
|
|
49
|
+
export const setActiveRoute = defineJsonRoute({ method: 'post', path: '/api/active', parse: parseSetActive, handle: handleSetActive }, { requireSameOrigin: true });
|
|
50
|
+
/**
|
|
51
|
+
* `GET /api/active` — reads back the current resource focus, resolving a
|
|
52
|
+
* display name for it, or `{active: false}` if nothing is set or the
|
|
53
|
+
* recorded focus has aged past {@link ACTIVE_CONTEXT_TTL_MS}.
|
|
54
|
+
*/
|
|
55
|
+
export const getActiveRoute = defineJsonRoute({ method: 'get', path: '/api/active', parse: () => ok({ value: undefined }), handle: handleGetActive }, { requireSameOrigin: true });
|
|
56
|
+
/** Mounts both active-context routes on `app`, sharing one in-memory store between them. A pack's `http(app, services)` calls this directly. */
|
|
57
|
+
export function registerActiveContextRoutes({ app, deps, adapter }, _optional = {}) {
|
|
58
|
+
const store = { current: null };
|
|
59
|
+
const routeDeps = {
|
|
60
|
+
store,
|
|
61
|
+
resolveResource: deps.resolveResource,
|
|
62
|
+
now: deps.now ?? (() => Date.now()),
|
|
63
|
+
};
|
|
64
|
+
mountJsonRoute({ app, spec: setActiveRoute, deps: routeDeps, adapter });
|
|
65
|
+
mountJsonRoute({ app, spec: getActiveRoute, deps: routeDeps, adapter });
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=active-context.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module agents
|
|
3
|
+
*
|
|
4
|
+
* `GET /api/agents` — lists the agents a host can expose to a client,
|
|
5
|
+
* including host-probed availability/model metadata when supplied.
|
|
6
|
+
* `POST /api/agents/rescan` asks the host to invalidate its discovery cache
|
|
7
|
+
* and probe again.
|
|
8
|
+
*
|
|
9
|
+
* `listAgents` is injected (matching `daemon-status.ts`/`active-context.ts`'s
|
|
10
|
+
* DI convention) rather than this module importing `@jini-ai/agent-runtime`
|
|
11
|
+
* directly — a host typically already has that package's `AGENT_DEFS` array
|
|
12
|
+
* in scope and just needs to project it, and this keeps this route pack from
|
|
13
|
+
* depending on subprocess discovery. The host owns probing,
|
|
14
|
+
* timeouts, caching, PATH/env policy, and the projection of spawn-only
|
|
15
|
+
* metadata; this transport only serializes the safe summary.
|
|
16
|
+
*/
|
|
17
|
+
import type { Express } from 'express';
|
|
18
|
+
import { type AdapterContext } from '@jini-ai/http-kit';
|
|
19
|
+
export interface AgentModelSummary {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly label: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Client-safe agent discovery data. Optional probe fields preserve the
|
|
25
|
+
* original static-registry contract for hosts that only expose `{id, name}`.
|
|
26
|
+
* Spawn internals (`bin`, resolved path, argv builders, env) never cross HTTP.
|
|
27
|
+
*/
|
|
28
|
+
export interface AgentSummary {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly available?: boolean;
|
|
32
|
+
readonly version?: string | null;
|
|
33
|
+
readonly authStatus?: 'ok' | 'missing' | 'unknown';
|
|
34
|
+
readonly models?: readonly AgentModelSummary[];
|
|
35
|
+
readonly reasoningOptions?: readonly AgentModelSummary[];
|
|
36
|
+
readonly modelsSource?: 'live' | 'fallback';
|
|
37
|
+
readonly supportsCustomModel?: boolean;
|
|
38
|
+
readonly diagnostic?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Whether this runtime can receive external MCP servers (the host application's or this engine's
|
|
41
|
+
* own tools) at all, per
|
|
42
|
+
* `@jini-ai/agent-runtime`'s `runtimeSupportsExternalTools(def)` — the def-level source of
|
|
43
|
+
* truth. `undefined` means the host did not populate this field (pre-existing hosts on an older
|
|
44
|
+
* `@jini-ai/daemon/http` continue to omit it); a UI gating on this should treat `undefined` the same
|
|
45
|
+
* as `true` (no known reason to warn) rather than assuming the worst.
|
|
46
|
+
*/
|
|
47
|
+
readonly supportsTools?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface AgentsHttpDeps {
|
|
50
|
+
/** Returns the host's cached or freshly resolved client-safe agent inventory. */
|
|
51
|
+
readonly listAgents: () => Promise<readonly AgentSummary[]> | readonly AgentSummary[];
|
|
52
|
+
/** Forces host-owned discovery to run again. Falls back to `listAgents` when omitted. */
|
|
53
|
+
readonly rescanAgents?: () => Promise<readonly AgentSummary[]> | readonly AgentSummary[];
|
|
54
|
+
}
|
|
55
|
+
export interface AgentListResponse {
|
|
56
|
+
readonly agents: readonly AgentSummary[];
|
|
57
|
+
}
|
|
58
|
+
/** `GET /api/agents` — read-only, no side effects; matches `runListRoute`/`runStatusRoute`'s posture of not requiring same-origin. */
|
|
59
|
+
export declare const agentListRoute: import("@jini-ai/http-kit").JsonRouteSpec<void, AgentListResponse, AgentsHttpDeps>;
|
|
60
|
+
/** `POST /api/agents/rescan` — explicit state refresh, protected by the local same-origin gate. */
|
|
61
|
+
export declare const agentRescanRoute: import("@jini-ai/http-kit").JsonRouteSpec<void, AgentListResponse, AgentsHttpDeps>;
|
|
62
|
+
/** Mounts the read and explicit-rescan agent discovery routes. */
|
|
63
|
+
export declare function registerAgentRoutes({ app, deps, adapter }: {
|
|
64
|
+
readonly app: Express;
|
|
65
|
+
readonly deps: AgentsHttpDeps;
|
|
66
|
+
readonly adapter: AdapterContext;
|
|
67
|
+
}, _optional?: Record<string, never>): void;
|
|
68
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineJsonRoute, mountJsonRoute } from '@jini-ai/http-kit';
|
|
2
|
+
import { ok } from '@jini-ai/http-kit';
|
|
3
|
+
/** `GET /api/agents` — read-only, no side effects; matches `runListRoute`/`runStatusRoute`'s posture of not requiring same-origin. */
|
|
4
|
+
export const agentListRoute = defineJsonRoute({ method: 'get', path: '/api/agents', parse: () => ok({ value: undefined }), handle: async ({ input: _input, deps }) => ok({ value: { agents: await deps.listAgents() } }) });
|
|
5
|
+
/** `POST /api/agents/rescan` — explicit state refresh, protected by the local same-origin gate. */
|
|
6
|
+
export const agentRescanRoute = defineJsonRoute({ method: 'post', path: '/api/agents/rescan', parse: () => ok({ value: undefined }), handle: async ({ input: _input, deps }) => ok({ value: { agents: await (deps.rescanAgents ?? deps.listAgents)() } }) }, { requireSameOrigin: true });
|
|
7
|
+
/** Mounts the read and explicit-rescan agent discovery routes. */
|
|
8
|
+
export function registerAgentRoutes({ app, deps, adapter }, _optional = {}) {
|
|
9
|
+
mountJsonRoute({ app, spec: agentListRoute, deps, adapter });
|
|
10
|
+
mountJsonRoute({ app, spec: agentRescanRoute, deps, adapter });
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=agents.js.map
|