@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.
Files changed (159) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +50 -8
  3. package/dist/agent-executor/contracts.d.ts +736 -0
  4. package/dist/agent-executor/contracts.js +28 -0
  5. package/dist/agent-executor/event-translation.d.ts +87 -0
  6. package/dist/agent-executor/event-translation.js +230 -0
  7. package/dist/agent-executor/events.d.ts +190 -0
  8. package/dist/agent-executor/events.js +848 -0
  9. package/dist/agent-executor/handoff.d.ts +18 -0
  10. package/dist/agent-executor/handoff.js +16 -0
  11. package/dist/agent-executor/index.d.ts +98 -0
  12. package/dist/agent-executor/index.js +98 -0
  13. package/dist/agent-executor/launch.d.ts +375 -0
  14. package/dist/agent-executor/launch.js +1001 -0
  15. package/dist/agent-executor/mcp-bridge.d.ts +338 -0
  16. package/dist/agent-executor/mcp-bridge.js +835 -0
  17. package/dist/agent-executor/values.d.ts +5 -0
  18. package/dist/agent-executor/values.js +13 -0
  19. package/dist/agent-executor.d.ts +375 -176
  20. package/dist/agent-executor.js +388 -335
  21. package/dist/close-status.d.ts +11 -8
  22. package/dist/close-status.js +31 -19
  23. package/dist/continuation/continuation-transport.d.ts +3 -1
  24. package/dist/continuation/continuation-transport.js +1 -1
  25. package/dist/continuation/journal.d.ts +12 -5
  26. package/dist/continuation/journal.js +4 -4
  27. package/dist/continuation/run-scoped-context-store.d.ts +13 -4
  28. package/dist/continuation/run-scoped-context-store.js +6 -6
  29. package/dist/continuation/run-start-handler.d.ts +1 -1
  30. package/dist/continuation/run-start-handler.js +3 -16
  31. package/dist/delegated-tool-bridge.d.ts +7 -3
  32. package/dist/delegated-tool-bridge.js +32 -31
  33. package/dist/event-log.d.ts +7 -5
  34. package/dist/event-log.js +12 -11
  35. package/dist/frontend-capability-tools.d.ts +1 -1
  36. package/dist/frontend-capability-tools.js +3 -2
  37. package/dist/frontend-session-registry.d.ts +32 -9
  38. package/dist/frontend-session-registry.js +12 -11
  39. package/dist/http/active-context.d.ts +105 -0
  40. package/dist/http/active-context.js +67 -0
  41. package/dist/http/agents.d.ts +68 -0
  42. package/dist/http/agents.js +12 -0
  43. package/dist/http/attachments.d.ts +618 -0
  44. package/dist/http/attachments.js +1034 -0
  45. package/dist/http/cancel-owned-runs.d.ts +49 -0
  46. package/dist/http/cancel-owned-runs.js +35 -0
  47. package/dist/http/component-catalog.d.ts +84 -0
  48. package/dist/http/component-catalog.js +64 -0
  49. package/dist/http/connectors.d.ts +209 -0
  50. package/dist/http/connectors.js +409 -0
  51. package/dist/http/daemon-status.d.ts +76 -0
  52. package/dist/http/daemon-status.js +55 -0
  53. package/dist/http/db-ops.d.ts +123 -0
  54. package/dist/http/db-ops.js +189 -0
  55. package/dist/http/delegated-tools.d.ts +113 -0
  56. package/dist/http/delegated-tools.js +230 -0
  57. package/dist/http/frontend-control.d.ts +136 -0
  58. package/dist/http/frontend-control.js +79 -0
  59. package/dist/http/frontend-sessions.d.ts +110 -0
  60. package/dist/http/frontend-sessions.js +180 -0
  61. package/dist/http/health.d.ts +95 -0
  62. package/dist/http/health.js +40 -0
  63. package/dist/http/host-tools.d.ts +214 -0
  64. package/dist/http/host-tools.js +329 -0
  65. package/dist/http/index.d.ts +52 -0
  66. package/dist/http/index.js +27 -0
  67. package/dist/http/media.d.ts +146 -0
  68. package/dist/http/media.js +346 -0
  69. package/dist/http/memory.d.ts +265 -0
  70. package/dist/http/memory.js +328 -0
  71. package/dist/http/model-proxy.d.ts +49 -0
  72. package/dist/http/model-proxy.js +642 -0
  73. package/dist/http/remote-run-events.d.ts +70 -0
  74. package/dist/http/remote-run-events.js +224 -0
  75. package/dist/http/research.d.ts +48 -0
  76. package/dist/http/research.js +315 -0
  77. package/dist/http/route-manifest.d.ts +35 -0
  78. package/dist/http/route-manifest.js +103 -0
  79. package/dist/http/routines.d.ts +120 -0
  80. package/dist/http/routines.js +298 -0
  81. package/dist/http/run-credentials.d.ts +73 -0
  82. package/dist/http/run-credentials.js +71 -0
  83. package/dist/http/runs.d.ts +91 -0
  84. package/dist/http/runs.js +221 -0
  85. package/dist/http/terminals.d.ts +138 -0
  86. package/dist/http/terminals.js +325 -0
  87. package/dist/http/tool-catalog.d.ts +78 -0
  88. package/dist/http/tool-catalog.js +66 -0
  89. package/dist/http/workspace-root.d.ts +77 -0
  90. package/dist/http/workspace-root.js +63 -0
  91. package/dist/http/xai.d.ts +90 -0
  92. package/dist/http/xai.js +655 -0
  93. package/dist/image-prompt-delivery.d.ts +13 -3
  94. package/dist/image-prompt-delivery.js +5 -5
  95. package/dist/index.d.ts +556 -4
  96. package/dist/index.js +556 -4
  97. package/dist/legacy-data-migration.d.ts +59 -8
  98. package/dist/legacy-data-migration.js +38 -33
  99. package/dist/read-only-tools.d.ts +130 -0
  100. package/dist/read-only-tools.js +115 -0
  101. package/dist/remote-tool-bridge.d.ts +11 -5
  102. package/dist/remote-tool-bridge.js +15 -15
  103. package/dist/routines/index.d.ts +1 -1
  104. package/dist/routines/index.js +1 -1
  105. package/dist/routines/routine-store.d.ts +34 -12
  106. package/dist/routines/routine-store.js +21 -20
  107. package/dist/routines/schedule.d.ts +48 -10
  108. package/dist/routines/schedule.js +21 -21
  109. package/dist/routines/scheduler.d.ts +32 -11
  110. package/dist/routines/scheduler.js +61 -57
  111. package/dist/routines/types.d.ts +10 -3
  112. package/dist/run/core/result.d.ts +6 -2
  113. package/dist/run/core/result.js +3 -3
  114. package/dist/run/core/retry.d.ts +17 -4
  115. package/dist/run/core/retry.js +7 -11
  116. package/dist/run/diagnostics/diagnostics.d.ts +14 -4
  117. package/dist/run/diagnostics/diagnostics.js +7 -7
  118. package/dist/run/index.d.ts +1 -1
  119. package/dist/run/index.js +1 -1
  120. package/dist/run-credentials.d.ts +138 -0
  121. package/dist/run-credentials.js +152 -0
  122. package/dist/run-lifecycle.d.ts +92 -33
  123. package/dist/run-lifecycle.js +81 -74
  124. package/dist/scheduler.d.ts +8 -0
  125. package/dist/scheduler.js +2 -0
  126. package/dist/session-coordination.d.ts +115 -0
  127. package/dist/session-coordination.js +111 -0
  128. package/dist/store/agent-sessions/errors.d.ts +8 -0
  129. package/dist/store/agent-sessions/errors.js +10 -0
  130. package/dist/store/agent-sessions/index.d.ts +12 -0
  131. package/dist/store/agent-sessions/index.js +19 -0
  132. package/dist/store/agent-sessions/pglite.d.ts +12 -0
  133. package/dist/store/agent-sessions/pglite.js +7 -0
  134. package/dist/store/agent-sessions/ports.d.ts +24 -0
  135. package/dist/store/agent-sessions/ports.js +2 -0
  136. package/dist/store/agent-sessions/postgres.d.ts +12 -0
  137. package/dist/store/agent-sessions/postgres.js +7 -0
  138. package/dist/store/agent-sessions/schema.d.ts +3 -0
  139. package/dist/store/agent-sessions/schema.js +23 -0
  140. package/dist/store/agent-sessions/sql.d.ts +35 -0
  141. package/dist/store/agent-sessions/sql.js +59 -0
  142. package/dist/store/agent-sessions/sqlite.d.ts +117 -0
  143. package/dist/store/agent-sessions/sqlite.js +128 -0
  144. package/dist/store/event-log/sqlite.d.ts +75 -0
  145. package/dist/store/event-log/sqlite.js +230 -0
  146. package/dist/surface-exchanges.d.ts +97 -0
  147. package/dist/surface-exchanges.js +153 -0
  148. package/dist/terminal-session.d.ts +43 -11
  149. package/dist/terminal-session.js +22 -20
  150. package/dist/tokens.js +4 -4
  151. package/dist/tool-audit.d.ts +114 -0
  152. package/dist/tool-audit.js +95 -0
  153. package/dist/tool-executor.d.ts +22 -7
  154. package/dist/tool-executor.js +16 -16
  155. package/dist/tool-result-media.d.ts +3 -1
  156. package/dist/tool-result-media.js +1 -1
  157. package/dist/tool-result-surfaces.d.ts +3 -1
  158. package/dist/tool-result-surfaces.js +1 -1
  159. 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(descriptor: FrontendSessionDescriptor, deliver: (invocation: FrontendInvocation) => void): FrontendSessionHandle;
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(runId: string, sessionId: string): () => void;
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(runId: string, bindToken: string): () => void;
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(runId: string, capabilityId: string, input: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>;
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(sessionId: string, invocationId: string, outcome: FrontendOutcome): boolean;
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(runId: string): readonly string[];
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(runId: string): FrontendSessionDescriptor | undefined;
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(options?: CreateFrontendSessionRegistryOptions): FrontendSessionRegistry;
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(options = {}) {
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