@hasna-internal/kai-cordis-client-runner 0.1.1-rc.2
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 +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +68 -0
- package/README.zh.md +68 -0
- package/lib/client.js +4219 -0
- package/lib/index.js +11 -0
- package/lib/invariant.js +26 -0
- package/lib/types/client/api-catalog.d.ts +96 -0
- package/lib/types/client/evaluator.d.ts +67 -0
- package/lib/types/client/guard.d.ts +52 -0
- package/lib/types/client/index.d.ts +113 -0
- package/lib/types/client/inspect-registry.d.ts +67 -0
- package/lib/types/client/orchestrator.d.ts +131 -0
- package/lib/types/client/providers.d.ts +13 -0
- package/lib/types/client/runtime.d.ts +228 -0
- package/lib/types/client/slot-catalog.d.ts +67 -0
- package/lib/types/client/timer.d.ts +84 -0
- package/lib/types/index.d.ts +9 -0
- package/lib/types/invariant.d.ts +16 -0
- package/package.json +76 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,4219 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@hasna-internal/kai-cordis-client-runner",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
//#region \0rolldown/runtime.js
|
|
8
|
+
var __create = Object.create;
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
16
|
+
key = keys[i];
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
18
|
+
get: ((k) => from[k]).bind(null, key),
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
25
|
+
value: mod,
|
|
26
|
+
enumerable: true
|
|
27
|
+
}) : target, mod));
|
|
28
|
+
//#endregion
|
|
29
|
+
let react = require("react");
|
|
30
|
+
react = __toESM(react, 1);
|
|
31
|
+
let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
|
|
32
|
+
//#region lib/types/client/evaluator.js
|
|
33
|
+
/**
|
|
34
|
+
* Browser-half closure evaluation: the package source runs as the body of an
|
|
35
|
+
* async function whose parameters ARE the symbol surface. Shadowing parameters
|
|
36
|
+
* (setTimeout/fetch/require/…) turn the ambient browser globals into teaching
|
|
37
|
+
* redirects without touching the page. The host syntax-prechecked the source at
|
|
38
|
+
* define time; SyntaxError handling here is the engine-divergence fallback and
|
|
39
|
+
* reaches the model through the load report.
|
|
40
|
+
*/
|
|
41
|
+
const TIMER_REDIRECT = "browser timer globals are unavailable in dynamic packages. Declare inject: ['timer'] on the returned plugin, query Client Service.listService for the exact API, and close over that plugin ctx. In React, create timers from an event handler or React.useEffect and return callback-form disposers from the effect cleanup.";
|
|
42
|
+
/**
|
|
43
|
+
* Where each withheld browser global sends the author instead. One home for two
|
|
44
|
+
* consumers: the closure traps below throw these, and a render crash whose
|
|
45
|
+
* message names one of them gets the same redirect appended — a package that
|
|
46
|
+
* reached the global some other way (`window.setInterval`) crashes with the
|
|
47
|
+
* engine's own bare text, and the author needs the redirect either way.
|
|
48
|
+
*/
|
|
49
|
+
const DYNAMIC_CLIENT_REDIRECTS = {
|
|
50
|
+
setTimeout: TIMER_REDIRECT,
|
|
51
|
+
setInterval: TIMER_REDIRECT,
|
|
52
|
+
clearTimeout: TIMER_REDIRECT,
|
|
53
|
+
clearInterval: TIMER_REDIRECT,
|
|
54
|
+
fetch: "network belongs to the HOST half: register a handler there with harness.handle(method, fn) and call it here via host.call(method, args).",
|
|
55
|
+
require: "modules cannot be imported here. React arrives as the `React` closure symbol; everything else goes through ctx services or host.call."
|
|
56
|
+
};
|
|
57
|
+
/** Callable teaching traps shadowing the ambient globals the closure must not reach. */
|
|
58
|
+
function closureTraps() {
|
|
59
|
+
const traps = {};
|
|
60
|
+
for (const [name, redirect] of Object.entries(DYNAMIC_CLIENT_REDIRECTS)) traps[name] = () => {
|
|
61
|
+
throw new Error(`${name} is not available in a dynamic client half — ${redirect}`);
|
|
62
|
+
};
|
|
63
|
+
return traps;
|
|
64
|
+
}
|
|
65
|
+
/** The `harness` seat exists only host-side; any touch teaches the split. */
|
|
66
|
+
function harnessTrap() {
|
|
67
|
+
return new Proxy({}, { get(_target, prop) {
|
|
68
|
+
throw new Error(`harness.${String(prop)} belongs to the HOST half (\`code\`): register handlers there with harness.handle(method, fn); the browser half calls them via host.call(method, args).`);
|
|
69
|
+
} });
|
|
70
|
+
}
|
|
71
|
+
/** Per-package style-tag bookkeeping behind the `styles.insert` symbol. */
|
|
72
|
+
var DynamicCordisStyles = class {
|
|
73
|
+
pluginId;
|
|
74
|
+
tags = /* @__PURE__ */ new Set();
|
|
75
|
+
/** @param pluginId - owning Plugin ID, stamped as `data-dyn` on every tag. */
|
|
76
|
+
constructor(pluginId) {
|
|
77
|
+
this.pluginId = pluginId;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Inject one stylesheet, removed automatically on package unload.
|
|
81
|
+
* @param css - raw CSS text.
|
|
82
|
+
* @returns disposer removing this one tag early.
|
|
83
|
+
*/
|
|
84
|
+
insert(css) {
|
|
85
|
+
if (typeof css !== "string") throw new Error("styles.insert(css) needs a CSS string");
|
|
86
|
+
const tag = document.createElement("style");
|
|
87
|
+
tag.dataset.dyn = this.pluginId;
|
|
88
|
+
tag.textContent = css;
|
|
89
|
+
document.head.append(tag);
|
|
90
|
+
this.tags.add(tag);
|
|
91
|
+
return () => {
|
|
92
|
+
this.tags.delete(tag);
|
|
93
|
+
tag.remove();
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** Live tag count (load-report contribution summary). */
|
|
97
|
+
get count() {
|
|
98
|
+
return this.tags.size;
|
|
99
|
+
}
|
|
100
|
+
/** Remove every tag this package still owns (unload path). */
|
|
101
|
+
dispose() {
|
|
102
|
+
for (const tag of this.tags) tag.remove();
|
|
103
|
+
this.tags.clear();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
/** Stringify one console argument for the error mirror. */
|
|
107
|
+
function errorText(arg) {
|
|
108
|
+
if (arg instanceof Error) return arg.message;
|
|
109
|
+
if (typeof arg === "string") return arg;
|
|
110
|
+
if (arg === void 0) return "undefined";
|
|
111
|
+
try {
|
|
112
|
+
return JSON.stringify(arg);
|
|
113
|
+
} catch {
|
|
114
|
+
return "[unserializable console argument]";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Tagged write-through console; error lines additionally copy into the load report. */
|
|
118
|
+
function taggedConsole(pluginId, noteError) {
|
|
119
|
+
const tag = `[cordis:${pluginId}]`;
|
|
120
|
+
const forward = (level) => (...args) => {
|
|
121
|
+
console[level](tag, ...args);
|
|
122
|
+
if (level !== "error") return;
|
|
123
|
+
noteError(args.map(errorText).join(" ").slice(0, 500));
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
...console,
|
|
127
|
+
log: forward("log"),
|
|
128
|
+
info: forward("info"),
|
|
129
|
+
warn: forward("warn"),
|
|
130
|
+
error: forward("error"),
|
|
131
|
+
debug: forward("debug")
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Narrow a closure return value to a mountable plugin (host guard mirror).
|
|
136
|
+
* @param value - whatever the closure returned.
|
|
137
|
+
* @returns whether the value is mountable.
|
|
138
|
+
*/
|
|
139
|
+
function isDynamicCordisPlugin(value) {
|
|
140
|
+
if (typeof value === "function") return true;
|
|
141
|
+
return typeof value === "object" && value !== null && typeof value.apply === "function";
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Evaluate one package's browser half and return the (un-guarded) plugin.
|
|
145
|
+
* @param pluginId - stable Plugin ID (console tag and style ownership).
|
|
146
|
+
* @param clientCode - the browser half's source: an async function body returning a plugin.
|
|
147
|
+
* @param env - runner wiring for `host.call` and error mirroring.
|
|
148
|
+
* @param styles - the package's style bookkeeping (owned by the caller so unload can dispose it).
|
|
149
|
+
* @returns the plugin the closure returned.
|
|
150
|
+
* @throws teaching errors for syntax failures and non-plugin returns.
|
|
151
|
+
*/
|
|
152
|
+
async function evaluateClientHalf(pluginId, clientCode, env, styles) {
|
|
153
|
+
const traps = closureTraps();
|
|
154
|
+
const parameters = [
|
|
155
|
+
"React",
|
|
156
|
+
"console",
|
|
157
|
+
"styles",
|
|
158
|
+
"host",
|
|
159
|
+
"harness",
|
|
160
|
+
...Object.keys(traps),
|
|
161
|
+
"process",
|
|
162
|
+
"Buffer"
|
|
163
|
+
];
|
|
164
|
+
let closure;
|
|
165
|
+
try {
|
|
166
|
+
closure = new Function(...parameters, `return (async () => {\n${clientCode}\n})()`);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
169
|
+
throw new Error(`client half failed to parse in this browser: ${error.message}\nThe browser half is plain JavaScript (no JSX, no TypeScript); build elements with React.createElement.`);
|
|
170
|
+
}
|
|
171
|
+
const returned = await closure(react, taggedConsole(pluginId, (message) => {
|
|
172
|
+
env.noteError(message);
|
|
173
|
+
}), styles, {
|
|
174
|
+
/**
|
|
175
|
+
* Call a host-half handler of THIS package (harness.handle pairing). A call
|
|
176
|
+
* with nothing to pass omits the argument: it arrives at the handler as
|
|
177
|
+
* `null`, because the wire carries JSON and `undefined` is not JSON —
|
|
178
|
+
* requiring `host.call('m', {})` would be a ritual, and defaulting to `{}`
|
|
179
|
+
* would invent an empty argument the caller never wrote.
|
|
180
|
+
*/
|
|
181
|
+
call: (method, args = null) => env.invoke(method, args) }, harnessTrap(), ...Object.values(traps), void 0, void 0);
|
|
182
|
+
if (!isDynamicCordisPlugin(returned)) {
|
|
183
|
+
if (returned === void 0) throw new Error("client half returned `undefined` — did you forget `return`?\n ✓ return (ctx) => { … }\n ✓ return { name: '…', inject: ['slots'], apply(ctx) { … } }");
|
|
184
|
+
throw new Error("client half must `return` a plugin: a function, or an object with an `apply(ctx)` method");
|
|
185
|
+
}
|
|
186
|
+
return returned;
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region lib/types/client/guard.js
|
|
190
|
+
/**
|
|
191
|
+
* The browser twin of the tool-cordis context facade: a whitelist of
|
|
192
|
+
* lifecycle-safe verbs plus optional `ctx.get()` lookup and declared-service
|
|
193
|
+
* property access, with
|
|
194
|
+
* framework internals withheld and Context-valued returns denied. Two seats
|
|
195
|
+
* carry extra machinery: `slots`, where the register proxy assigns the
|
|
196
|
+
* shadowing priority and ledgers the registration — invoking the service with
|
|
197
|
+
* the traced receiver so the effect lands on the CALLING plugin's fiber
|
|
198
|
+
* (SlotRegistry.register must stay a prototype method for exactly that
|
|
199
|
+
* reason) — and `theme`, whose override source is pinned to the package id.
|
|
200
|
+
*
|
|
201
|
+
* This is API discipline, not a security boundary: a dynamic package's code is
|
|
202
|
+
* as trusted as the host process that accepted its definition.
|
|
203
|
+
*/
|
|
204
|
+
/** Facade verbs beyond declared services (host CTX_VERBS twin). */
|
|
205
|
+
const CTX_VERBS = new Set([
|
|
206
|
+
"effect",
|
|
207
|
+
"on",
|
|
208
|
+
"once",
|
|
209
|
+
"provide",
|
|
210
|
+
"timeout",
|
|
211
|
+
"interval",
|
|
212
|
+
"setTimeout",
|
|
213
|
+
"setInterval",
|
|
214
|
+
"throttle",
|
|
215
|
+
"debounce"
|
|
216
|
+
]);
|
|
217
|
+
const TIMER_VERBS = new Set([
|
|
218
|
+
"timeout",
|
|
219
|
+
"interval",
|
|
220
|
+
"setTimeout",
|
|
221
|
+
"setInterval",
|
|
222
|
+
"throttle",
|
|
223
|
+
"debounce"
|
|
224
|
+
]);
|
|
225
|
+
/** Reject any service return that is a cordis Context (host guard twin). */
|
|
226
|
+
function denyContext(value, service, env) {
|
|
227
|
+
if (value instanceof _deepseek_ai_cordis.Context) return rejectGuard(env, `service "${service}" returned a cordis Context, which the dynamic facade does not expose. Operate through your own plugin ctx and the services you declared — never another context.`);
|
|
228
|
+
return value;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Forward service methods with the traced service as receiver — `this.ctx`
|
|
232
|
+
* inside prototype methods (slots.register) must stay the CALLER's ctx so
|
|
233
|
+
* effects land on the calling plugin's fiber — while denying Context returns.
|
|
234
|
+
*/
|
|
235
|
+
function guardedService(service, name, env) {
|
|
236
|
+
return new Proxy(service, { get(target, prop) {
|
|
237
|
+
const value = Reflect.get(target, prop, target);
|
|
238
|
+
if (typeof value !== "function") return denyContext(value, name, env);
|
|
239
|
+
return (...args) => {
|
|
240
|
+
const result = Reflect.apply(value, target, args);
|
|
241
|
+
if (result instanceof Promise) return result.then((resolved) => denyContext(resolved, name, env));
|
|
242
|
+
return denyContext(result, name, env);
|
|
243
|
+
};
|
|
244
|
+
} });
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* The slots seat: automatic shadowing priority and ledger recording around the
|
|
248
|
+
* traced service's own register.
|
|
249
|
+
*/
|
|
250
|
+
function guardedSlots(slots, env) {
|
|
251
|
+
return new Proxy(slots, { get(target, prop) {
|
|
252
|
+
const value = Reflect.get(target, prop, target);
|
|
253
|
+
if (prop !== "register") {
|
|
254
|
+
if (typeof value !== "function") return denyContext(value, "slots", env);
|
|
255
|
+
return (...args) => denyContext(Reflect.apply(value, target, args), "slots", env);
|
|
256
|
+
}
|
|
257
|
+
return (rawOptions, component) => {
|
|
258
|
+
if (typeof rawOptions !== "object" || rawOptions === null) return rejectGuard(env, "slots.register(options, component) needs an options object with a `name`");
|
|
259
|
+
const options = { ...rawOptions };
|
|
260
|
+
const slot = options.name;
|
|
261
|
+
if (typeof slot !== "string" || slot.length === 0) return rejectGuard(env, "slots.register options need a string `name` (the target slot key)");
|
|
262
|
+
if (slot === "tool.view.cordis") {
|
|
263
|
+
if (options.key !== "self") return rejectGuard(env, "tool.view.cordis only accepts key \"self\"; the runtime binds it to this Package");
|
|
264
|
+
options.key = `${env.pkg.pluginId}.${env.pkg.packageId}`;
|
|
265
|
+
}
|
|
266
|
+
const spec = slots.spec(slot);
|
|
267
|
+
let priority = options.priority;
|
|
268
|
+
if (spec === void 0 || spec.kind !== "chain") {
|
|
269
|
+
priority = env.allocatePriority();
|
|
270
|
+
options.priority = priority;
|
|
271
|
+
}
|
|
272
|
+
const dispose = Reflect.get(target, "register", target).call(target, options, component);
|
|
273
|
+
env.ledger.push({
|
|
274
|
+
slot,
|
|
275
|
+
priority
|
|
276
|
+
});
|
|
277
|
+
env.claim(component);
|
|
278
|
+
return dispose;
|
|
279
|
+
};
|
|
280
|
+
} });
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* The theme seat: `overrideTokens`' source is FORCED to the package id — a
|
|
284
|
+
* dynamic package can never impersonate (or evict) another source's layer, and
|
|
285
|
+
* its own layers converge under one identity unload can reason about. The
|
|
286
|
+
* layer's disposer is additionally hung on the calling fiber, because the
|
|
287
|
+
* documented contract is "unload restores" and model code cannot be trusted to
|
|
288
|
+
* keep the returned handle (slots parity — register hangs its own cleanup).
|
|
289
|
+
* Everything else forwards through the generic guard.
|
|
290
|
+
*/
|
|
291
|
+
function guardedTheme(theme, env, ctx) {
|
|
292
|
+
return new Proxy(theme, { get(target, prop) {
|
|
293
|
+
if (prop !== "overrideTokens") {
|
|
294
|
+
const value = Reflect.get(target, prop, target);
|
|
295
|
+
if (typeof value !== "function") return denyContext(value, "theme", env);
|
|
296
|
+
return (...args) => {
|
|
297
|
+
const result = Reflect.apply(value, target, args);
|
|
298
|
+
if (result instanceof Promise) return result.then((resolved) => denyContext(resolved, "theme", env));
|
|
299
|
+
return denyContext(result, "theme", env);
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
return (source, tokens) => {
|
|
303
|
+
if (tokens === void 0 && typeof source === "object" && source !== null) return rejectGuard(env, "theme.overrideTokens(source, tokens) takes two arguments; source is replaced with your package id, so pass any string first and the token map second: overrideTokens('mine', { '--dsw-alias-…': { light: '…', dark: '…' } })");
|
|
304
|
+
const method = Reflect.get(target, "overrideTokens", target);
|
|
305
|
+
const dispose = Reflect.apply(method, target, [`${env.pkg.pluginId}.${env.pkg.packageId}`, tokens]);
|
|
306
|
+
ctx.effect(() => dispose, "cordis-client-runner: dynamic theme override layer");
|
|
307
|
+
return dispose;
|
|
308
|
+
};
|
|
309
|
+
} });
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Build the facade one dynamic plugin's `apply` receives (host sandboxContext
|
|
313
|
+
* twin, browser seats). `ctx.get(name)` performs optional lookup; direct
|
|
314
|
+
* `ctx.serviceName` access is gated by the fiber's `inject` declaration.
|
|
315
|
+
* @param ctx - the plugin's real fiber ctx (loader-created).
|
|
316
|
+
* @param env - package row + ledger sink.
|
|
317
|
+
* @returns the whitelisting proxy standing in for ctx.
|
|
318
|
+
*/
|
|
319
|
+
function dynamicCordisContext(ctx, env) {
|
|
320
|
+
const declared = new Set(Object.keys(ctx.fiber.inject));
|
|
321
|
+
const denyRead = (prop) => {
|
|
322
|
+
if (ctx.get(prop) !== void 0) return rejectGuard(env, `service "${prop}" is not declared by your plugin. Declare it on the plugin you return: { inject: ['${prop}', …], apply(ctx) { … } } — a plain \`function\` has no declaration site, so use the object form. The runtime then parks the package if the provider unloads.`);
|
|
323
|
+
return rejectGuard(env, `dynamic ctx does not expose "${prop}". Available: ctx.on / ctx.provide / timer helpers after injecting timer, and any service your returned plugin declared in inject (slots and theme are the usual UI seats). Framework internals are withheld by design.`);
|
|
324
|
+
};
|
|
325
|
+
const readService = (name, requireDeclaration) => {
|
|
326
|
+
if (requireDeclaration && !declared.has(name)) return denyRead(name);
|
|
327
|
+
const service = denyContext(ctx.get(name), name, env);
|
|
328
|
+
if (service === null || typeof service !== "object" && typeof service !== "function") return service;
|
|
329
|
+
if (name === "slots") return guardedSlots(service, env);
|
|
330
|
+
if (name === "theme") return guardedTheme(service, env, ctx);
|
|
331
|
+
return guardedService(service, name, env);
|
|
332
|
+
};
|
|
333
|
+
return new Proxy({}, {
|
|
334
|
+
get(_target, prop) {
|
|
335
|
+
if (prop === "get") return (name) => readService(name, false);
|
|
336
|
+
if (typeof prop !== "string") return void 0;
|
|
337
|
+
if (CTX_VERBS.has(prop)) return (...args) => {
|
|
338
|
+
if (TIMER_VERBS.has(prop) && !declared.has("timer")) return denyRead("timer");
|
|
339
|
+
const method = ctx[prop];
|
|
340
|
+
return Reflect.apply(method, ctx, args);
|
|
341
|
+
};
|
|
342
|
+
return readService(prop, true);
|
|
343
|
+
},
|
|
344
|
+
set(_target, prop) {
|
|
345
|
+
return rejectGuard(env, `dynamic ctx is read-only; cannot assign "${String(prop)}"`);
|
|
346
|
+
},
|
|
347
|
+
has: (_target, prop) => prop === "get" || typeof prop === "string" && (CTX_VERBS.has(prop) && (!TIMER_VERBS.has(prop) || declared.has("timer")) || declared.has(prop))
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function rejectGuard(env, message) {
|
|
351
|
+
const error = new Error(message);
|
|
352
|
+
env.reportFailure(error);
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region lib/types/client/runtime.js
|
|
357
|
+
/**
|
|
358
|
+
* Per-package browser lifecycle: evaluate the closure, wrap `apply` in the guard
|
|
359
|
+
* facade, seat a ready-made factory in the module table, and create a loader
|
|
360
|
+
* entry — so dynamic packages ride the exact machinery static plugins do
|
|
361
|
+
* (activation gating on inject, fiber-effect cleanup, status projection). Unload
|
|
362
|
+
* = loader entry removal (fiber disposal cascades slot entries and facade
|
|
363
|
+
* effects) + factory invalidation + style removal.
|
|
364
|
+
*
|
|
365
|
+
* The engine answers its caller: `load` resolves with what this page ended up
|
|
366
|
+
* with, which is what the run orchestration reports back to the host. Loads
|
|
367
|
+
* converge by Plugin Run ID against live state, not history: loading the exact
|
|
368
|
+
* activation this page already runs is a no-op that still answers, another run
|
|
369
|
+
* replaces it, and the same Package after a retract loads afresh. Per-Plugin
|
|
370
|
+
* serialization keeps a second request from interleaving with one in flight.
|
|
371
|
+
*/
|
|
372
|
+
/** Module-table id of one package (also its loader entry name and fiber name). */
|
|
373
|
+
function moduleIdOf(id) {
|
|
374
|
+
return `dyn/${id}`;
|
|
375
|
+
}
|
|
376
|
+
/** The browser-side load engine for dynamic packages. */
|
|
377
|
+
var DynamicCordisPackageRunner = class {
|
|
378
|
+
env;
|
|
379
|
+
live = /* @__PURE__ */ new Map();
|
|
380
|
+
/** Serializes load/unload per package id (a second request can outrun a slow load). */
|
|
381
|
+
queues = /* @__PURE__ */ new Map();
|
|
382
|
+
changeListeners = /* @__PURE__ */ new Set();
|
|
383
|
+
/** Page-local shadowing rank. A later registration receives a lower priority. */
|
|
384
|
+
nextPriority = 0;
|
|
385
|
+
/**
|
|
386
|
+
* Which package seated which component, and for whom. Component identity is the
|
|
387
|
+
* only attribution key that holds:
|
|
388
|
+
* - the registry stores the component verbatim, so a crashed entry carries its
|
|
389
|
+
* own way back — no parallel entry ledger to keep in step;
|
|
390
|
+
* - `entry.registrant` is `options.registrant ?? fiber.name` and the facade does
|
|
391
|
+
* not strip a package-supplied one, so a package could name itself something
|
|
392
|
+
* else — attributing by it would let a package impersonate another;
|
|
393
|
+
* - the assigned shadowing priority is unique but absent on chain entries (their
|
|
394
|
+
* election is deliberately left alone), so it would miss chain crashes;
|
|
395
|
+
* - a package torn down between the crash and the report is still attributable,
|
|
396
|
+
* because this index does not depend on the live record.
|
|
397
|
+
*
|
|
398
|
+
* Two packages cannot collide here: each browser half is evaluated in its own
|
|
399
|
+
* closure, so no component object reaches two of them. A collision is only
|
|
400
|
+
* possible inside ONE package (the same component seated twice), where both
|
|
401
|
+
* entries map to the same id and the value is identical.
|
|
402
|
+
*/
|
|
403
|
+
owners = /* @__PURE__ */ new WeakMap();
|
|
404
|
+
/** This page's last render crash per package: what a run surface shows on the row. */
|
|
405
|
+
failures = /* @__PURE__ */ new Map();
|
|
406
|
+
unwatch;
|
|
407
|
+
snapshotCache;
|
|
408
|
+
failureCache;
|
|
409
|
+
/** @param env - loader/module/slot wiring plus the two host verbs this engine uses. */
|
|
410
|
+
constructor(env) {
|
|
411
|
+
this.env = env;
|
|
412
|
+
this.unwatch = env.slots.onEntryError((slot, entry, error, info) => {
|
|
413
|
+
const component = entry.component;
|
|
414
|
+
const owner = indexable(component) ? this.owners.get(component) : void 0;
|
|
415
|
+
if (owner === void 0) return;
|
|
416
|
+
const details = errorDetails(error);
|
|
417
|
+
const failure = {
|
|
418
|
+
slot,
|
|
419
|
+
message: renderFailureMessage(slot, details.message),
|
|
420
|
+
...details.stack === void 0 ? {} : { stack: details.stack },
|
|
421
|
+
abdicated: info.abdicated
|
|
422
|
+
};
|
|
423
|
+
env.reportRenderFailure(owner.agentId, owner.pluginId, owner.pluginRunId, failure);
|
|
424
|
+
this.failures.set(owner.pluginId, failure);
|
|
425
|
+
this.notify();
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Observe live-set changes (the run-state surface's re-render seam).
|
|
430
|
+
* @param fn - notified after every converged mutation.
|
|
431
|
+
* @returns unsubscribe.
|
|
432
|
+
*/
|
|
433
|
+
subscribe(fn) {
|
|
434
|
+
this.changeListeners.add(fn);
|
|
435
|
+
return () => {
|
|
436
|
+
this.changeListeners.delete(fn);
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* This page's last render crash per package, on the same notification channel as
|
|
441
|
+
* the live set — a surface that already subscribed learns about a crash without
|
|
442
|
+
* a second mechanism to wire.
|
|
443
|
+
*/
|
|
444
|
+
renderFailures = {
|
|
445
|
+
getSnapshot: () => this.failureCache ??= new Map(this.failures),
|
|
446
|
+
subscribe: (fn) => this.subscribe(fn)
|
|
447
|
+
};
|
|
448
|
+
/**
|
|
449
|
+
* What this page currently has loaded (stable reference between mutations, so
|
|
450
|
+
* it can back a snapshot selector).
|
|
451
|
+
* @returns one row per live package.
|
|
452
|
+
*/
|
|
453
|
+
getSnapshot() {
|
|
454
|
+
return this.snapshotCache ??= [...this.live.values()].map(({ pkg, ledger, styles }) => ({
|
|
455
|
+
pluginId: pkg.pluginId,
|
|
456
|
+
packageId: pkg.packageId,
|
|
457
|
+
pluginRunId: pkg.pluginRunId,
|
|
458
|
+
name: pkg.name,
|
|
459
|
+
slots: [...new Set(ledger.map((row) => row.slot))],
|
|
460
|
+
styleCount: styles.count
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Whether this page has the browser half loaded — page-local truth, never the
|
|
465
|
+
* host's "it is running".
|
|
466
|
+
* @param pluginId - stable Plugin identity.
|
|
467
|
+
* @returns true while one activation of the Plugin is live here.
|
|
468
|
+
*/
|
|
469
|
+
isLoaded(pluginId) {
|
|
470
|
+
return this.live.has(pluginId);
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Load one browser half into this page and answer what happened.
|
|
474
|
+
* @param half - source for one exact Host activation.
|
|
475
|
+
* @returns the outcome the run orchestration reports to the host.
|
|
476
|
+
*/
|
|
477
|
+
load(half) {
|
|
478
|
+
return this.enqueue(half.pluginId, async () => {
|
|
479
|
+
const current = this.live.get(half.pluginId);
|
|
480
|
+
if (current !== void 0) {
|
|
481
|
+
if (current.pkg.pluginRunId === half.pluginRunId) return settled(current);
|
|
482
|
+
await this.teardown(current.pkg.pluginId, current.entryId, current.styles);
|
|
483
|
+
}
|
|
484
|
+
const result = await this.mount(half);
|
|
485
|
+
this.notify();
|
|
486
|
+
return result;
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Unload one package (`cordis/dynamic-retract`: a stop, or an undefine
|
|
491
|
+
* that stops first).
|
|
492
|
+
* @param pluginId - stable Plugin identity.
|
|
493
|
+
* @param pluginRunId - exact activation being retracted; a newer run survives.
|
|
494
|
+
*/
|
|
495
|
+
retract(pluginId, pluginRunId) {
|
|
496
|
+
this.enqueue(pluginId, async () => {
|
|
497
|
+
const current = this.live.get(pluginId);
|
|
498
|
+
if (current === void 0 || current.pkg.pluginRunId !== pluginRunId) return;
|
|
499
|
+
await this.teardown(pluginId, current.entryId, current.styles);
|
|
500
|
+
this.notify();
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
/** Unload everything (plugin disposal path). */
|
|
504
|
+
async dispose() {
|
|
505
|
+
this.unwatch();
|
|
506
|
+
for (const current of [...this.live.values()]) await this.teardown(current.pkg.pluginId, current.entryId, current.styles);
|
|
507
|
+
this.notify();
|
|
508
|
+
}
|
|
509
|
+
notify() {
|
|
510
|
+
this.snapshotCache = void 0;
|
|
511
|
+
this.failureCache = void 0;
|
|
512
|
+
for (const fn of [...this.changeListeners]) fn();
|
|
513
|
+
}
|
|
514
|
+
/** Queue one package operation behind that package's previous ones. */
|
|
515
|
+
enqueue(id, op) {
|
|
516
|
+
const next = (this.queues.get(id) ?? Promise.resolve()).then(op);
|
|
517
|
+
this.queues.set(id, next.then(() => {}, () => {}));
|
|
518
|
+
return next;
|
|
519
|
+
}
|
|
520
|
+
async mount(half) {
|
|
521
|
+
const styles = new DynamicCordisStyles(half.pluginId);
|
|
522
|
+
const ledger = [];
|
|
523
|
+
let plugin;
|
|
524
|
+
try {
|
|
525
|
+
plugin = await evaluateClientHalf(half.pluginId, half.code, {
|
|
526
|
+
invoke: (method, args) => this.env.invoke(half.pluginId, half.pluginRunId, method, args),
|
|
527
|
+
noteError: (message) => {
|
|
528
|
+
console.error(`[cordis-client-runner] ${half.pluginId} logged an error:`, message);
|
|
529
|
+
}
|
|
530
|
+
}, styles);
|
|
531
|
+
} catch (error) {
|
|
532
|
+
styles.dispose();
|
|
533
|
+
return {
|
|
534
|
+
ok: false,
|
|
535
|
+
cause: "evaluate",
|
|
536
|
+
...errorDetails(error),
|
|
537
|
+
error
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
const pkg = {
|
|
541
|
+
pluginId: half.pluginId,
|
|
542
|
+
packageId: half.packageId,
|
|
543
|
+
pluginRunId: half.pluginRunId,
|
|
544
|
+
name: half.name
|
|
545
|
+
};
|
|
546
|
+
const surface = this.guardedSurface(pkg, half.agentId, plugin, ledger);
|
|
547
|
+
const moduleId = moduleIdOf(half.pluginId);
|
|
548
|
+
this.env.modules.invalidate(moduleId);
|
|
549
|
+
const sink = globalThis.__ModuleLoader__;
|
|
550
|
+
if (sink === void 0) throw new Error("cordis-client-runner: window.__ModuleLoader__ is missing (booted outside the web shell?)");
|
|
551
|
+
sink.load({
|
|
552
|
+
id: moduleId,
|
|
553
|
+
factory: () => surface
|
|
554
|
+
});
|
|
555
|
+
const entryId = await this.env.loader.create({ name: moduleId });
|
|
556
|
+
const fiber = this.env.loader.resolve(entryId).fiber;
|
|
557
|
+
if (fiber === void 0) {
|
|
558
|
+
await this.teardown(half.pluginId, entryId, styles);
|
|
559
|
+
return {
|
|
560
|
+
ok: false,
|
|
561
|
+
cause: "module-import",
|
|
562
|
+
message: "module import failed (see the browser console)"
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
await fiber.await();
|
|
567
|
+
} catch (error) {
|
|
568
|
+
await this.teardown(half.pluginId, entryId, styles);
|
|
569
|
+
return {
|
|
570
|
+
ok: false,
|
|
571
|
+
cause: "activate",
|
|
572
|
+
...errorDetails(error),
|
|
573
|
+
error
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
const record = {
|
|
577
|
+
pkg,
|
|
578
|
+
entryId,
|
|
579
|
+
styles,
|
|
580
|
+
ledger,
|
|
581
|
+
waitingFor: Object.keys(fiber.inject).filter((name) => this.env.ctx.get(name) === void 0)
|
|
582
|
+
};
|
|
583
|
+
this.live.set(half.pluginId, record);
|
|
584
|
+
this.failures.delete(half.pluginId);
|
|
585
|
+
return settled(record);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Wrap the evaluated plugin so `apply` sees the guard facade; the surface
|
|
589
|
+
* doubles as the module-table module. The plugin's OWN `inject` survives (the
|
|
590
|
+
* object form's declaration is the facade's service gate, mirroring the host
|
|
591
|
+
* sandbox reading `ctx.fiber.inject`); the function form has no declaration
|
|
592
|
+
* site and therefore reaches no service.
|
|
593
|
+
*/
|
|
594
|
+
guardedSurface(pkg, agentId, plugin, ledger) {
|
|
595
|
+
const claim = (component) => {
|
|
596
|
+
if (indexable(component)) this.owners.set(component, {
|
|
597
|
+
pluginId: pkg.pluginId,
|
|
598
|
+
pluginRunId: pkg.pluginRunId,
|
|
599
|
+
agentId
|
|
600
|
+
});
|
|
601
|
+
};
|
|
602
|
+
const guarded = (ctx) => dynamicCordisContext(ctx, {
|
|
603
|
+
pkg,
|
|
604
|
+
ledger,
|
|
605
|
+
claim,
|
|
606
|
+
allocatePriority: () => --this.nextPriority,
|
|
607
|
+
reportFailure: (error) => {
|
|
608
|
+
this.env.reportGuardFailure(agentId, pkg.pluginId, pkg.pluginRunId, errorDetails(error));
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
if (typeof plugin === "function") return {
|
|
612
|
+
name: moduleIdOf(pkg.pluginId),
|
|
613
|
+
apply: (ctx) => plugin(guarded(ctx))
|
|
614
|
+
};
|
|
615
|
+
return {
|
|
616
|
+
...plugin,
|
|
617
|
+
name: moduleIdOf(pkg.pluginId),
|
|
618
|
+
apply: (ctx, config) => plugin.apply(guarded(ctx), config)
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Unload one package's contributions. Takes the pieces rather than the record
|
|
623
|
+
* because a load can fail before any record is seated.
|
|
624
|
+
*/
|
|
625
|
+
async teardown(id, entryId, styles) {
|
|
626
|
+
this.live.delete(id);
|
|
627
|
+
this.failures.delete(id);
|
|
628
|
+
await this.env.loader.remove(entryId);
|
|
629
|
+
this.env.modules.invalidate(moduleIdOf(id));
|
|
630
|
+
styles.dispose();
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
/** The success answer for a package that is live here, parked or active. */
|
|
634
|
+
function settled(record) {
|
|
635
|
+
return {
|
|
636
|
+
ok: true,
|
|
637
|
+
pluginRunId: record.pkg.pluginRunId,
|
|
638
|
+
...record.waitingFor.length > 0 ? { waitingFor: record.waitingFor } : {}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Whether a component can key the ownership index. Identity is the key, so only
|
|
643
|
+
* objects and functions qualify — a package may register anything, and what it
|
|
644
|
+
* registered is what a crash report carries back.
|
|
645
|
+
* @param component - whatever a package passed as its component.
|
|
646
|
+
* @returns true when the value can be indexed by identity.
|
|
647
|
+
*/
|
|
648
|
+
function indexable(component) {
|
|
649
|
+
return typeof component === "object" && component !== null || typeof component === "function";
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Preserve error fields for a load result without fabricating a stack.
|
|
653
|
+
* @param error - original thrown value.
|
|
654
|
+
* @returns its message and original string stack, when present.
|
|
655
|
+
*/
|
|
656
|
+
function errorDetails(error) {
|
|
657
|
+
if (typeof error !== "object" || error === null) return { message: String(error) };
|
|
658
|
+
const message = "message" in error && typeof error.message === "string" ? error.message : Object.prototype.toString.call(error);
|
|
659
|
+
const stack = "stack" in error && typeof error.stack === "string" ? error.stack : void 0;
|
|
660
|
+
return {
|
|
661
|
+
message,
|
|
662
|
+
...stack === void 0 ? {} : { stack }
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* What the authoring session reads about one render crash. The slot says where it
|
|
667
|
+
* happened, the crash message says what broke, and a withheld global named in that
|
|
668
|
+
* text pulls in its redirect — a package that reached `window.setInterval` around
|
|
669
|
+
* the closure trap crashes with the engine's bare message, which teaches nothing.
|
|
670
|
+
*/
|
|
671
|
+
function renderFailureMessage(slot, message) {
|
|
672
|
+
const redirect = Object.entries(DYNAMIC_CLIENT_REDIRECTS).find(([name, text]) => message.includes(name) && !message.includes(text))?.[1];
|
|
673
|
+
return `your entry in slot "${slot}" crashed while React rendered it: ${message}` + (redirect === void 0 ? "" : `\n${redirect}`);
|
|
674
|
+
}
|
|
675
|
+
//#endregion
|
|
676
|
+
//#region lib/types/client/orchestrator.js
|
|
677
|
+
/**
|
|
678
|
+
* Page-side run orchestration for model approvals and direct panel gestures.
|
|
679
|
+
* Host activation always precedes Client loading. The same Plugin-keyed state
|
|
680
|
+
* drives every surface, so remounting a panel never loses an open approval or
|
|
681
|
+
* an in-flight transition.
|
|
682
|
+
*/
|
|
683
|
+
/** Drives Host → Client activation and publishes Plugin-keyed activity. */
|
|
684
|
+
var CordisRunOrchestrator = class {
|
|
685
|
+
env;
|
|
686
|
+
requests = /* @__PURE__ */ new Map();
|
|
687
|
+
activity = /* @__PURE__ */ new Map();
|
|
688
|
+
failures = /* @__PURE__ */ new Map();
|
|
689
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
690
|
+
listeners = /* @__PURE__ */ new Set();
|
|
691
|
+
activityCache;
|
|
692
|
+
failureCache;
|
|
693
|
+
/** @param env - Client loader and folded Host operations. */
|
|
694
|
+
constructor(env) {
|
|
695
|
+
this.env = env;
|
|
696
|
+
}
|
|
697
|
+
/** Open approvals and current activation attempts, keyed by stable Plugin ID. */
|
|
698
|
+
activeRuns = {
|
|
699
|
+
getSnapshot: () => this.activityCache ??= new Map(this.activity),
|
|
700
|
+
subscribe: (fn) => this.observe(fn)
|
|
701
|
+
};
|
|
702
|
+
/** Latest page-side activation failure for each Plugin. */
|
|
703
|
+
lastRunError = {
|
|
704
|
+
getSnapshot: () => this.failureCache ??= new Map(this.failures),
|
|
705
|
+
subscribe: (fn) => this.observe(fn)
|
|
706
|
+
};
|
|
707
|
+
/**
|
|
708
|
+
* Register a Client activation request, starting it immediately when the Plugin is already authorized.
|
|
709
|
+
* @param request - forwarded approval and activation metadata.
|
|
710
|
+
*/
|
|
711
|
+
open(request) {
|
|
712
|
+
this.requests.set(request.requestId, request);
|
|
713
|
+
if (!request.requiresApproval) {
|
|
714
|
+
this.orchestrate({
|
|
715
|
+
agentId: request.agentId,
|
|
716
|
+
pluginId: request.pluginId,
|
|
717
|
+
packageId: request.packageId,
|
|
718
|
+
mode: request.mode,
|
|
719
|
+
requestId: request.requestId,
|
|
720
|
+
hasClientHalf: true
|
|
721
|
+
}).catch((error) => {
|
|
722
|
+
console.error(`[cordis-client-runner] automatic activation ${request.requestId} failed:`, error);
|
|
723
|
+
});
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
if (this.activity.get(request.pluginId)?.phase !== "orchestrating") this.activity.set(request.pluginId, {
|
|
727
|
+
phase: "awaiting-approval",
|
|
728
|
+
requestId: request.requestId,
|
|
729
|
+
agentId: request.agentId,
|
|
730
|
+
packageId: request.packageId,
|
|
731
|
+
mode: request.mode,
|
|
732
|
+
name: request.name,
|
|
733
|
+
purpose: request.purpose
|
|
734
|
+
});
|
|
735
|
+
this.commit();
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Rebuild pending approvals and automatic Client activations from an authoritative Host inventory read.
|
|
739
|
+
* @param rows - complete process-wide Plugin inventory.
|
|
740
|
+
*/
|
|
741
|
+
reconcileApprovals(rows) {
|
|
742
|
+
const expected = /* @__PURE__ */ new Map();
|
|
743
|
+
for (const row of rows) {
|
|
744
|
+
const attempt = row.latestRun;
|
|
745
|
+
if (attempt?.approvalRequestId === void 0 || attempt.status !== "awaiting-approval" && attempt.status !== "starting-host" && attempt.status !== "client-pending") continue;
|
|
746
|
+
const pkg = row.packages.find((candidate) => candidate.packageId === attempt.packageId);
|
|
747
|
+
if (pkg === void 0) continue;
|
|
748
|
+
expected.set(attempt.approvalRequestId, {
|
|
749
|
+
requestId: attempt.approvalRequestId,
|
|
750
|
+
agentId: row.agentId,
|
|
751
|
+
pluginId: row.pluginId,
|
|
752
|
+
packageId: attempt.packageId,
|
|
753
|
+
mode: attempt.mode,
|
|
754
|
+
name: pkg.name,
|
|
755
|
+
purpose: pkg.purpose,
|
|
756
|
+
requiresApproval: attempt.requiresApproval ?? attempt.status === "awaiting-approval"
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
let changed = false;
|
|
760
|
+
for (const [requestId, request] of [...this.requests]) {
|
|
761
|
+
if (expected.has(requestId)) continue;
|
|
762
|
+
this.requests.delete(requestId);
|
|
763
|
+
const current = this.activity.get(request.pluginId);
|
|
764
|
+
if (current?.phase === "awaiting-approval" && current.requestId === requestId) this.activity.delete(request.pluginId);
|
|
765
|
+
changed = true;
|
|
766
|
+
}
|
|
767
|
+
for (const [requestId, request] of expected) {
|
|
768
|
+
const previous = this.requests.get(requestId);
|
|
769
|
+
const current = this.activity.get(request.pluginId);
|
|
770
|
+
if (!request.requiresApproval && current?.phase === "orchestrating") continue;
|
|
771
|
+
if (request.requiresApproval && sameRequest(previous, request) && current?.phase === "awaiting-approval" && current.requestId === requestId) continue;
|
|
772
|
+
if (!request.requiresApproval) {
|
|
773
|
+
this.open(request);
|
|
774
|
+
changed = true;
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
this.requests.set(requestId, request);
|
|
778
|
+
if (current?.phase !== "orchestrating") this.activity.set(request.pluginId, {
|
|
779
|
+
phase: "awaiting-approval",
|
|
780
|
+
requestId,
|
|
781
|
+
agentId: request.agentId,
|
|
782
|
+
packageId: request.packageId,
|
|
783
|
+
mode: request.mode,
|
|
784
|
+
name: request.name,
|
|
785
|
+
purpose: request.purpose
|
|
786
|
+
});
|
|
787
|
+
changed = true;
|
|
788
|
+
}
|
|
789
|
+
if (changed) this.commit();
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Close an approval settled by another page or by cancellation.
|
|
793
|
+
* @param requestId - approval request that can no longer be answered here.
|
|
794
|
+
*/
|
|
795
|
+
close(requestId) {
|
|
796
|
+
const request = this.requests.get(requestId);
|
|
797
|
+
if (request === void 0) return;
|
|
798
|
+
this.requests.delete(requestId);
|
|
799
|
+
const current = this.activity.get(request.pluginId);
|
|
800
|
+
if (current?.phase === "awaiting-approval" && current.requestId === requestId) this.activity.delete(request.pluginId);
|
|
801
|
+
this.commit();
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Approve and execute one still-open model request.
|
|
805
|
+
* @param requestId - approval request to execute.
|
|
806
|
+
* @param approveFutureVersions - whether this approval covers later Packages for the same Plugin.
|
|
807
|
+
*/
|
|
808
|
+
approve(requestId, approveFutureVersions) {
|
|
809
|
+
const request = this.requests.get(requestId);
|
|
810
|
+
if (request === void 0 || !request.requiresApproval) return Promise.resolve();
|
|
811
|
+
return this.orchestrate({
|
|
812
|
+
agentId: request.agentId,
|
|
813
|
+
pluginId: request.pluginId,
|
|
814
|
+
packageId: request.packageId,
|
|
815
|
+
mode: request.mode,
|
|
816
|
+
requestId,
|
|
817
|
+
approveFutureVersions,
|
|
818
|
+
hasClientHalf: true
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Reject one still-open model request without executing either half.
|
|
823
|
+
* @param requestId - approval request to reject.
|
|
824
|
+
*/
|
|
825
|
+
async decline(requestId) {
|
|
826
|
+
const request = this.requests.get(requestId);
|
|
827
|
+
if (request === void 0 || !request.requiresApproval) return;
|
|
828
|
+
const current = this.activity.get(request.pluginId);
|
|
829
|
+
if (current?.phase !== "awaiting-approval" || current.requestId !== requestId) return;
|
|
830
|
+
this.requests.delete(requestId);
|
|
831
|
+
this.activity.delete(request.pluginId);
|
|
832
|
+
this.commit();
|
|
833
|
+
await this.answer(requestId, {
|
|
834
|
+
ok: false,
|
|
835
|
+
reason: "rejected"
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Execute a direct panel run; the user gesture itself authorizes it.
|
|
840
|
+
* @param request - exact Package activation selected by the user.
|
|
841
|
+
*/
|
|
842
|
+
startUserRun(request) {
|
|
843
|
+
return this.orchestrate(request);
|
|
844
|
+
}
|
|
845
|
+
observe(fn) {
|
|
846
|
+
this.listeners.add(fn);
|
|
847
|
+
return () => {
|
|
848
|
+
this.listeners.delete(fn);
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
commit() {
|
|
852
|
+
this.activityCache = void 0;
|
|
853
|
+
this.failureCache = void 0;
|
|
854
|
+
for (const fn of [...this.listeners]) fn();
|
|
855
|
+
}
|
|
856
|
+
orchestrate(plan) {
|
|
857
|
+
const running = this.inFlight.get(plan.pluginId);
|
|
858
|
+
if (running !== void 0) return running;
|
|
859
|
+
this.activity.set(plan.pluginId, {
|
|
860
|
+
phase: "orchestrating",
|
|
861
|
+
agentId: plan.agentId,
|
|
862
|
+
packageId: plan.packageId,
|
|
863
|
+
mode: plan.mode
|
|
864
|
+
});
|
|
865
|
+
this.failures.delete(plan.pluginId);
|
|
866
|
+
if (plan.requestId !== void 0) this.requests.delete(plan.requestId);
|
|
867
|
+
this.commit();
|
|
868
|
+
const attempt = this.drive(plan).finally(() => {
|
|
869
|
+
this.inFlight.delete(plan.pluginId);
|
|
870
|
+
this.activity.delete(plan.pluginId);
|
|
871
|
+
this.commit();
|
|
872
|
+
});
|
|
873
|
+
this.inFlight.set(plan.pluginId, attempt);
|
|
874
|
+
return attempt;
|
|
875
|
+
}
|
|
876
|
+
async drive(plan) {
|
|
877
|
+
const started = await this.startHost(plan);
|
|
878
|
+
if (!started.ok) {
|
|
879
|
+
this.fail(plan, "host-half-failed", started);
|
|
880
|
+
if (plan.requestId !== void 0) await this.answer(plan.requestId, {
|
|
881
|
+
...started,
|
|
882
|
+
reason: "host-half-failed"
|
|
883
|
+
});
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
if (!plan.hasClientHalf) return;
|
|
887
|
+
let source;
|
|
888
|
+
try {
|
|
889
|
+
source = await this.env.host.getClientCode(plan.agentId, plan.pluginId, started.pluginRunId);
|
|
890
|
+
} catch (error) {
|
|
891
|
+
await this.finishClientFailure(plan, started.pluginRunId, started.startedHere, errorDetails(error), error);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const loaded = await this.env.runner.load({
|
|
895
|
+
pluginId: source.pluginId,
|
|
896
|
+
packageId: source.packageId,
|
|
897
|
+
pluginRunId: source.pluginRunId,
|
|
898
|
+
agentId: plan.agentId,
|
|
899
|
+
name: source.name,
|
|
900
|
+
code: source.code
|
|
901
|
+
}).catch((error) => ({
|
|
902
|
+
ok: false,
|
|
903
|
+
cause: "evaluate",
|
|
904
|
+
...errorDetails(error),
|
|
905
|
+
error
|
|
906
|
+
}));
|
|
907
|
+
if (!loaded.ok) {
|
|
908
|
+
await this.finishClientFailure(plan, started.pluginRunId, started.startedHere, {
|
|
909
|
+
message: `${loaded.cause}: ${loaded.message}`,
|
|
910
|
+
...loaded.stack === void 0 ? {} : { stack: loaded.stack }
|
|
911
|
+
}, loaded.error);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
const resolution = {
|
|
915
|
+
ok: true,
|
|
916
|
+
pluginRunId: loaded.pluginRunId,
|
|
917
|
+
...loaded.waitingFor === void 0 ? {} : { waitingFor: loaded.waitingFor }
|
|
918
|
+
};
|
|
919
|
+
if (plan.requestId !== void 0) {
|
|
920
|
+
await this.answer(plan.requestId, resolution);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
await this.settleDirect(plan, resolution);
|
|
924
|
+
}
|
|
925
|
+
async startHost(plan) {
|
|
926
|
+
try {
|
|
927
|
+
return await this.env.host.runHostHalf(plan.agentId, plan.pluginId, plan.packageId, plan.mode, plan.requestId ?? null, plan.approveFutureVersions ?? false);
|
|
928
|
+
} catch (error) {
|
|
929
|
+
return {
|
|
930
|
+
ok: false,
|
|
931
|
+
...errorDetails(error)
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
async finishClientFailure(plan, pluginRunId, startedHere, failure, originalError) {
|
|
936
|
+
console.error(`[cordis-client-runner] Client activation ${plan.pluginId}/${plan.packageId} (${pluginRunId}) failed:`, originalError ?? failure);
|
|
937
|
+
this.fail(plan, "client-half-failed", failure);
|
|
938
|
+
const resolution = {
|
|
939
|
+
ok: false,
|
|
940
|
+
reason: "client-half-failed",
|
|
941
|
+
pluginRunId,
|
|
942
|
+
startedHere,
|
|
943
|
+
...failure
|
|
944
|
+
};
|
|
945
|
+
if (plan.requestId !== void 0) await this.answer(plan.requestId, resolution);
|
|
946
|
+
else await this.settleDirect(plan, resolution);
|
|
947
|
+
}
|
|
948
|
+
async settleDirect(plan, resolution) {
|
|
949
|
+
try {
|
|
950
|
+
const response = await this.env.host.settleUserRun(plan.agentId, plan.pluginId, resolution);
|
|
951
|
+
if (!response.ok) this.fail(plan, "client-half-failed", response);
|
|
952
|
+
} catch (error) {
|
|
953
|
+
this.fail(plan, "client-half-failed", errorDetails(error));
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
async answer(requestId, resolution) {
|
|
957
|
+
try {
|
|
958
|
+
await this.env.host.resolveRequestRun(requestId, resolution);
|
|
959
|
+
} catch (error) {
|
|
960
|
+
console.error(`[cordis-client-runner] answering run request ${requestId} failed:`, error);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
fail(plan, reason, failure) {
|
|
964
|
+
this.failures.set(plan.pluginId, {
|
|
965
|
+
packageId: plan.packageId,
|
|
966
|
+
reason,
|
|
967
|
+
...failure
|
|
968
|
+
});
|
|
969
|
+
this.commit();
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
function sameRequest(left, right) {
|
|
973
|
+
return left?.requestId === right.requestId && left.agentId === right.agentId && left.pluginId === right.pluginId && left.packageId === right.packageId && left.mode === right.mode && left.name === right.name && left.purpose === right.purpose && left.requiresApproval === right.requiresApproval;
|
|
974
|
+
}
|
|
975
|
+
//#endregion
|
|
976
|
+
//#region lib/types/client/inspect-registry.js
|
|
977
|
+
/** Browser registry for read-only Cordis capability providers. */
|
|
978
|
+
/** Client provider registry, manifest publisher, and live query dispatcher. */
|
|
979
|
+
var ClientCordisInspectRegistry = class {
|
|
980
|
+
host;
|
|
981
|
+
providers = /* @__PURE__ */ new Map();
|
|
982
|
+
active = /* @__PURE__ */ new Map();
|
|
983
|
+
publishQueued = false;
|
|
984
|
+
syncChain = Promise.resolve();
|
|
985
|
+
/** @param host - folded manifest and query result transport. */
|
|
986
|
+
constructor(host) {
|
|
987
|
+
this.host = host;
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Register one Client provider and publish a new complete manifest.
|
|
991
|
+
* @param registration - provider manifest and local handler.
|
|
992
|
+
* @returns idempotent disposer.
|
|
993
|
+
*/
|
|
994
|
+
register(registration) {
|
|
995
|
+
const { manifest } = registration;
|
|
996
|
+
if (manifest.id.trim() === "") throw new Error("Client Cordis inspect provider id must not be empty");
|
|
997
|
+
if (this.providers.has(manifest.id)) throw new Error(`Client Cordis inspect provider "${manifest.id}" is already registered`);
|
|
998
|
+
const names = /* @__PURE__ */ new Set();
|
|
999
|
+
for (const method of manifest.methods) {
|
|
1000
|
+
if (names.has(method.name)) throw new Error(`Client Cordis inspect provider "${manifest.id}" repeats method "${method.name}"`);
|
|
1001
|
+
names.add(method.name);
|
|
1002
|
+
}
|
|
1003
|
+
this.providers.set(manifest.id, registration);
|
|
1004
|
+
this.publish();
|
|
1005
|
+
let disposed = false;
|
|
1006
|
+
return () => {
|
|
1007
|
+
if (disposed) return;
|
|
1008
|
+
disposed = true;
|
|
1009
|
+
if (this.providers.get(manifest.id) === registration) {
|
|
1010
|
+
this.providers.delete(manifest.id);
|
|
1011
|
+
this.publish();
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
/** Publish the current complete manifest, including after reconnect. */
|
|
1016
|
+
publish() {
|
|
1017
|
+
if (this.publishQueued) return;
|
|
1018
|
+
this.publishQueued = true;
|
|
1019
|
+
queueMicrotask(() => {
|
|
1020
|
+
this.publishQueued = false;
|
|
1021
|
+
const manifests = [...this.providers.values()].map((provider) => provider.manifest);
|
|
1022
|
+
this.syncChain = this.syncChain.then(async () => {
|
|
1023
|
+
await this.host.sync(manifests);
|
|
1024
|
+
}).catch((error) => {
|
|
1025
|
+
console.error("[cordis-client-runner] syncing inspect providers failed:", error);
|
|
1026
|
+
});
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Execute and answer one Host-broadcast query.
|
|
1031
|
+
* @param request - exact provider query and Session correlation received from Host.
|
|
1032
|
+
* @returns after the first local result has been sent back to Host.
|
|
1033
|
+
*/
|
|
1034
|
+
async query(request) {
|
|
1035
|
+
if (this.active.has(request.requestId)) return;
|
|
1036
|
+
const controller = new AbortController();
|
|
1037
|
+
this.active.set(request.requestId, controller);
|
|
1038
|
+
let resolution;
|
|
1039
|
+
try {
|
|
1040
|
+
const provider = this.providers.get(request.provider);
|
|
1041
|
+
if (provider === void 0) resolution = {
|
|
1042
|
+
ok: false,
|
|
1043
|
+
reason: "provider-missing",
|
|
1044
|
+
message: `Client inspect provider "${request.provider}" is unavailable`
|
|
1045
|
+
};
|
|
1046
|
+
else if (!provider.manifest.methods.some((method) => method.name === request.method)) resolution = {
|
|
1047
|
+
ok: false,
|
|
1048
|
+
reason: "method-missing",
|
|
1049
|
+
message: `Client inspect provider "${request.provider}" has no method "${request.method}"`
|
|
1050
|
+
};
|
|
1051
|
+
else {
|
|
1052
|
+
const data = await provider.query(request.method, request.input, {
|
|
1053
|
+
signal: controller.signal,
|
|
1054
|
+
sessionId: request.agentId
|
|
1055
|
+
});
|
|
1056
|
+
resolution = controller.signal.aborted ? {
|
|
1057
|
+
ok: false,
|
|
1058
|
+
reason: "cancelled",
|
|
1059
|
+
message: "Client inspect query was cancelled"
|
|
1060
|
+
} : {
|
|
1061
|
+
ok: true,
|
|
1062
|
+
data
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
resolution = controller.signal.aborted ? {
|
|
1067
|
+
ok: false,
|
|
1068
|
+
reason: "cancelled",
|
|
1069
|
+
message: "Client inspect query was cancelled"
|
|
1070
|
+
} : {
|
|
1071
|
+
ok: false,
|
|
1072
|
+
reason: "provider-error",
|
|
1073
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1074
|
+
};
|
|
1075
|
+
} finally {
|
|
1076
|
+
this.active.delete(request.requestId);
|
|
1077
|
+
}
|
|
1078
|
+
if (controller.signal.aborted) return;
|
|
1079
|
+
await this.host.resolve(request.agentId, request.requestId, resolution);
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Cancel local work after another page answered or the Tool call ended.
|
|
1083
|
+
* @param requestId - query correlation that is no longer answerable.
|
|
1084
|
+
*/
|
|
1085
|
+
close(requestId) {
|
|
1086
|
+
this.active.get(requestId)?.abort();
|
|
1087
|
+
this.active.delete(requestId);
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
/**
|
|
1091
|
+
* Provide the registry as a normal Client service.
|
|
1092
|
+
* @param ctx - Client Cordis context receiving the service.
|
|
1093
|
+
* @param registry - page-local inspect registry to publish.
|
|
1094
|
+
*/
|
|
1095
|
+
function provideClientCordisInspect(ctx, registry) {
|
|
1096
|
+
ctx.provide("cordisInspect", registry);
|
|
1097
|
+
}
|
|
1098
|
+
//#endregion
|
|
1099
|
+
//#region lib/types/client/api-catalog.js
|
|
1100
|
+
/**
|
|
1101
|
+
* Generated by scripts/gen-cordis-api.ts — do not edit by hand; run
|
|
1102
|
+
* `pnpm run gen-cordis-api` to regenerate (freshness-gated by
|
|
1103
|
+
* `pnpm run verify-cordis-api` in doc-sync).
|
|
1104
|
+
*
|
|
1105
|
+
* The machine-readable cordis API catalog `cordis_inspect` serves to the
|
|
1106
|
+
* model: harness services (summary + structured public method contracts),
|
|
1107
|
+
* harness events (mode + structured listener contracts), and the inherited `ctx` API. Produced by
|
|
1108
|
+
* the same AST walk as docs/cordis-catalog, so this data and the rendered
|
|
1109
|
+
* docs cannot diverge.
|
|
1110
|
+
*
|
|
1111
|
+
* @module @hasna-internal/kai-cordis-client-runner/client/api-catalog
|
|
1112
|
+
*/
|
|
1113
|
+
/** Every harness `ctx.<key>` service, sorted by key. */
|
|
1114
|
+
const SERVICE_API = [
|
|
1115
|
+
{
|
|
1116
|
+
key: "layout",
|
|
1117
|
+
summary: "The outward layout face (`ctx.layout`): the panel transitions other plugins may trigger — and exactly what a test fake must supply.",
|
|
1118
|
+
description: "The outward layout face (`ctx.layout`): the panel transitions other plugins may trigger — and exactly what a test fake must supply. The attachPanels wiring hook stays on the concrete class (root-entry assembly only).",
|
|
1119
|
+
methods: [
|
|
1120
|
+
{
|
|
1121
|
+
signature: "toggleSidebar(): void",
|
|
1122
|
+
description: "Toggle the sidebar panel (closed ⟷ contract default width).",
|
|
1123
|
+
parameters: []
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
signature: "openDetails(): void",
|
|
1127
|
+
description: "Open the details panel (no-op when already open).",
|
|
1128
|
+
parameters: []
|
|
1129
|
+
},
|
|
1130
|
+
{
|
|
1131
|
+
signature: "closeDetails(): void",
|
|
1132
|
+
description: "Close the details panel.",
|
|
1133
|
+
parameters: []
|
|
1134
|
+
}
|
|
1135
|
+
]
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
key: "locale",
|
|
1139
|
+
summary: "Dictionary registry plus locale preference.",
|
|
1140
|
+
description: "Dictionary registry plus locale preference. Lookup chain per key: the entry's namespace in the active locale -> that namespace's en fallback -> the shared common namespace (active, then en) -> the key itself (missing text stays visible, fail loud in the UI rather than blank). Reads go through getLocale; writes only through setLocale; continuous sync through the `locale/change` event, or through the LocaleFace getSnapshot/subscribe pair the render machinery consumes (installed via `ctx.slots.installLocale`).",
|
|
1141
|
+
methods: [
|
|
1142
|
+
{
|
|
1143
|
+
signature: "getLocale(): LocaleSnapshot",
|
|
1144
|
+
description: "Read the current immutable locale snapshot.",
|
|
1145
|
+
parameters: [],
|
|
1146
|
+
returns: "the current snapshot (stable reference until the next change)."
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
signature: "getSnapshot(): LocaleSnapshot",
|
|
1150
|
+
description: "LocaleFace getSnapshot: the current snapshot (carries `revision`; stable reference between changes, uSES-safe).",
|
|
1151
|
+
parameters: [],
|
|
1152
|
+
returns: "the current snapshot."
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
signature: "subscribe(fn: () => void): () => void",
|
|
1156
|
+
description: "LocaleFace subscribe: notified on every snapshot change (locale switch or dictionary registration — registrations bump the revision so already rendered outlets pick up late-arriving dictionaries).",
|
|
1157
|
+
parameters: [{
|
|
1158
|
+
name: "fn",
|
|
1159
|
+
description: "change callback."
|
|
1160
|
+
}],
|
|
1161
|
+
returns: "unsubscribe."
|
|
1162
|
+
},
|
|
1163
|
+
{
|
|
1164
|
+
signature: "setLocale(id: string): void",
|
|
1165
|
+
description: "Switch the active locale — the only user preference write entry.\n\nThe durable write happens even when the id already matches the active locale, because the active value may be a provisional browser-derived or fallback resolution that nothing has stored yet. Picking the language already on screen is still an explicit choice, and it must survive a different browser sharing the same DSH home. Only the render notification is conditional: republishing an unchanged locale would churn every subscriber for nothing.",
|
|
1166
|
+
parameters: [{
|
|
1167
|
+
name: "id",
|
|
1168
|
+
description: "a registered locale id; unknown ids throw."
|
|
1169
|
+
}]
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
signature: "register<N extends keyof LocaleNamespaceMap & string>(ns: N, dicts: Record<LocaleId, LocaleDictOf<N>>): () => void",
|
|
1173
|
+
description: "Register a declared namespace's dictionaries, all locales in one call — the typed form: each dictionary is checked against the namespace's LocaleNamespaceMap key union (a missing or extra key is a compile error), and every shipped locale is required (bilingual balance enforced at registration). Duplicate (ns, locale) throws (single occupant; a namespace's texts have one owner). Registration bumps the revision so mounted outlets pick up late-arriving dictionaries.",
|
|
1174
|
+
parameters: [{
|
|
1175
|
+
name: "ns",
|
|
1176
|
+
description: "a namespace merged into LocaleNamespaceMap."
|
|
1177
|
+
}, {
|
|
1178
|
+
name: "dicts",
|
|
1179
|
+
description: "complete dictionaries keyed by locale id."
|
|
1180
|
+
}],
|
|
1181
|
+
returns: "disposer removing every locale registered by this call (idempotent)."
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
signature: "register(ns: string, locale: string, dict: LocaleDict): () => void",
|
|
1185
|
+
description: "Single-locale untyped form for namespaces outside the merge table (dynamic composition, tests).",
|
|
1186
|
+
parameters: [
|
|
1187
|
+
{
|
|
1188
|
+
name: "ns",
|
|
1189
|
+
description: "namespace."
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
name: "locale",
|
|
1193
|
+
description: "locale tag."
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
name: "dict",
|
|
1197
|
+
description: "dictionary."
|
|
1198
|
+
}
|
|
1199
|
+
],
|
|
1200
|
+
returns: "disposer (idempotent)."
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
signature: "bind<N extends keyof LocaleNamespaceMap & string>(ns: N): TranslateNS<N>",
|
|
1204
|
+
description: "Bind a declared namespace to a translate function typed to its dictionary key union (plus the shared common vocabulary) — the same key domain the framework-injected `t` seat carries. The returned reference is stable per namespace (repeat binds return the same function), so it can ride inject surfaces without breaking memoization.",
|
|
1205
|
+
parameters: [{
|
|
1206
|
+
name: "ns",
|
|
1207
|
+
description: "a namespace merged into LocaleNamespaceMap."
|
|
1208
|
+
}],
|
|
1209
|
+
returns: "the typed translate function (reads the active locale at call time)."
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
signature: "bind(ns: string): Translate",
|
|
1213
|
+
description: "Untyped form for namespaces outside the merge table (dynamic composition, tests).",
|
|
1214
|
+
parameters: [{
|
|
1215
|
+
name: "ns",
|
|
1216
|
+
description: "namespace."
|
|
1217
|
+
}],
|
|
1218
|
+
returns: "the translate function."
|
|
1219
|
+
}
|
|
1220
|
+
]
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
key: "sessions",
|
|
1224
|
+
summary: "The sessions-service face injected as `ctx.sessions`.",
|
|
1225
|
+
description: "The sessions-service face injected as `ctx.sessions`.",
|
|
1226
|
+
methods: [
|
|
1227
|
+
{
|
|
1228
|
+
signature: "open(id: SessionId): void",
|
|
1229
|
+
description: "Select a session as current.",
|
|
1230
|
+
parameters: [{
|
|
1231
|
+
name: "id",
|
|
1232
|
+
description: "session id (must exist in the list; unknown ids fail loud)."
|
|
1233
|
+
}]
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
signature: "openSubagent(address: SubagentAddress): void",
|
|
1237
|
+
description: "Open a healthy catalog child through its exact direct-parent address.",
|
|
1238
|
+
parameters: [{
|
|
1239
|
+
name: "address",
|
|
1240
|
+
description: "catalog-derived parent and child ids."
|
|
1241
|
+
}]
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
signature: "setSubagentCatalogOpen(parentSessionId: SessionId, open: boolean): void",
|
|
1245
|
+
description: "Mark whether a catalog menu is consuming live membership updates.",
|
|
1246
|
+
parameters: [{
|
|
1247
|
+
name: "parentSessionId",
|
|
1248
|
+
description: "catalog owner."
|
|
1249
|
+
}, {
|
|
1250
|
+
name: "open",
|
|
1251
|
+
description: "current menu state."
|
|
1252
|
+
}]
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
signature: "refreshSubagents(parentSessionId: SessionId): Promise<void>",
|
|
1256
|
+
description: "Refresh one direct-child catalog.",
|
|
1257
|
+
parameters: [{
|
|
1258
|
+
name: "parentSessionId",
|
|
1259
|
+
description: "catalog owner."
|
|
1260
|
+
}],
|
|
1261
|
+
returns: "completion of the current or newly started refresh."
|
|
1262
|
+
},
|
|
1263
|
+
{
|
|
1264
|
+
signature: "search( query: string, signal: AbortSignal, ): Promise<RpcResult<{ items: SessionSearchResultItem[]; hasMore: boolean }>>",
|
|
1265
|
+
description: "Search the Host's visible message-content index. Results stay request-local; the list snapshot remains the metadata authority.",
|
|
1266
|
+
parameters: [{
|
|
1267
|
+
name: "query",
|
|
1268
|
+
description: "non-blank literal phrase."
|
|
1269
|
+
}, {
|
|
1270
|
+
name: "signal",
|
|
1271
|
+
description: "cancellation for a superseded search."
|
|
1272
|
+
}],
|
|
1273
|
+
returns: "bounded results, or a business/transport error."
|
|
1274
|
+
},
|
|
1275
|
+
{
|
|
1276
|
+
signature: "fork(opts: { sessionId: SessionId; atSeq?: number; increaseTitle?: boolean }): Promise<SessionId>",
|
|
1277
|
+
description: "Fork a session from a completed-turn prefix of the source; on resolution the child is in the list store and `open()` can target it.",
|
|
1278
|
+
parameters: [{
|
|
1279
|
+
name: "opts",
|
|
1280
|
+
description: "source session id, the optional event seq anchoring the cut (the boundary is the first turn/end at or after it; an in-log anchor in an open turn is unavailable rather than clipped backward), and whether to increment an inherited durable title before resolving."
|
|
1281
|
+
}],
|
|
1282
|
+
returns: "the child session id.",
|
|
1283
|
+
throws: ["when the fork fails, or when a requested child-title rename fails after creation."]
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
signature: "scope(id: SessionId): AgentContext | undefined",
|
|
1287
|
+
description: "Resolve an Agent-scoped context view (use-and-discard).",
|
|
1288
|
+
parameters: [{
|
|
1289
|
+
name: "id",
|
|
1290
|
+
description: "session id."
|
|
1291
|
+
}],
|
|
1292
|
+
returns: "scoped ctx, or undefined for a session neither listed nor already scoped."
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
signature: "binding(id: SessionId): SessionBinding | undefined",
|
|
1296
|
+
description: "Resolve the stable session binding (scope-addressed assembly feed).",
|
|
1297
|
+
parameters: [{
|
|
1298
|
+
name: "id",
|
|
1299
|
+
description: "session id."
|
|
1300
|
+
}],
|
|
1301
|
+
returns: "binding, or undefined for a session neither listed nor already scoped."
|
|
1302
|
+
}
|
|
1303
|
+
]
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
key: "slots",
|
|
1307
|
+
summary: "cordis Service layer of the slot system; see the module doc for the split with SlotCore.",
|
|
1308
|
+
description: "cordis Service layer of the slot system; see the module doc for the split with SlotCore.",
|
|
1309
|
+
methods: [{
|
|
1310
|
+
signature: "declare readonly register: SlotCore['register']",
|
|
1311
|
+
description: "The single registration API. The typed face IS the core's register (both overloads reused verbatim — one authority, no structural copy; see SlotCore.register for children declaration, store seat, inject face, load-time validation, and the unload cascade). This layer adds: disposal through the caller's ctx.effect (fiber unload = cascade), exclusive-factory minting (`store: createXxxStore` becomes a per-entry handle), the registrant diagnostics stamp, and store-instance lifecycle on the entry axis.\n\nDeclared here, implemented by prototype assignment below the class: it MUST stay a prototype method (never an instance arrow) — the cordis service proxy binds `this.ctx` to the CALLER's context at call time, which is what routes the effect (and the unload cascade) into the caller's fiber. An arrow property would freeze `this` to the service's own root ctx and silently break per-plugin disposal.",
|
|
1312
|
+
parameters: []
|
|
1313
|
+
}, {
|
|
1314
|
+
signature: "inject(key: keyof SlotMap & string, callback: () => SlotInjectionEffect): () => void",
|
|
1315
|
+
description: "Install an effect for each declaration lifetime of a slot. The callback runs synchronously when the declaration already exists; otherwise it runs inside the declaring `register()` call after the declaration is committed. Collapse disposes the effect and a later declaration runs it again. Callback effects are synchronous disposers; iterable effects install transactionally and dispose in reverse order. The controller belongs to the caller's fiber, so plugin unload cancels a pending wait and removes any active contribution.",
|
|
1316
|
+
parameters: [{
|
|
1317
|
+
name: "key",
|
|
1318
|
+
description: "declared SlotMap key to depend on."
|
|
1319
|
+
}, {
|
|
1320
|
+
name: "callback",
|
|
1321
|
+
description: "creates one disposer or an iterable of disposers."
|
|
1322
|
+
}],
|
|
1323
|
+
returns: "idempotent disposer for the wait and active effect.",
|
|
1324
|
+
throws: ["callback setup failures synchronously when the slot is already declared."]
|
|
1325
|
+
}]
|
|
1326
|
+
},
|
|
1327
|
+
{
|
|
1328
|
+
key: "theme",
|
|
1329
|
+
summary: "Theme registry and preference owner.",
|
|
1330
|
+
description: "Theme registry and preference owner. `light`/`dark` are built in (the base stylesheets carry both palettes); third-party themes register alias-layer overrides. Reads go through getTheme; preference writes only through setTheme; continuous sync only through the `theme/change` event. overrideTokens stacks partial token layers over the active theme without touching the registry. The service holds the `prefers-color-scheme` media query (environment sensing, not presentation) and re-emits when the OS scheme flips while the preference is `system`.",
|
|
1331
|
+
methods: [
|
|
1332
|
+
{
|
|
1333
|
+
signature: "getTheme(): ThemeSnapshot",
|
|
1334
|
+
description: "Read the current immutable theme snapshot.",
|
|
1335
|
+
parameters: [],
|
|
1336
|
+
returns: "the current snapshot (stable reference until the next change)."
|
|
1337
|
+
},
|
|
1338
|
+
{
|
|
1339
|
+
signature: "setTheme(id: string): void",
|
|
1340
|
+
description: "Switch the theme preference — the only user preference write entry. Built-in preferences are written through the settings scope and every accepted value emits `theme/change`.",
|
|
1341
|
+
parameters: [{
|
|
1342
|
+
name: "id",
|
|
1343
|
+
description: "a registered theme id or `system`; unknown ids throw."
|
|
1344
|
+
}]
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
signature: "register(definition: ThemeDefinition): () => void",
|
|
1348
|
+
description: "Register a theme. Duplicate id throws (single occupant per id; the built-in pair counts; `system` is a preference, not a registrable id).",
|
|
1349
|
+
parameters: [{
|
|
1350
|
+
name: "definition",
|
|
1351
|
+
description: "theme id, colorScheme, and alias-token overrides."
|
|
1352
|
+
}],
|
|
1353
|
+
returns: "disposer. Disposing the theme backing the active preference resets the preference to the default so the UI never keeps tokens of an unregistered theme."
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
signature: "overrideTokens(source: string, tokens: ThemeTokenOverrides): () => void",
|
|
1357
|
+
description: "Stack a token override layer on top of the active theme — the token-level analogue of slot shading: the base theme stays untouched, layers compose in seq order with later layers winning per-token, and removing a layer restores whatever it covered. Calling again with the same source replaces that source's whole layer and restacks it on top (effect re-registration semantics). Emits `theme/change` with the recomposed snapshot.",
|
|
1358
|
+
parameters: [{
|
|
1359
|
+
name: "source",
|
|
1360
|
+
description: "layer identity; one layer per source (dynamic packages pass their package id — the façade pins it, so it also names the layer's origin for inspection)."
|
|
1361
|
+
}, {
|
|
1362
|
+
name: "tokens",
|
|
1363
|
+
description: "token-name → `{ light, dark }` value pairs. Validated at runtime (model-authored callers reach this boundary with untyped JS); a bare string value throws a teaching error."
|
|
1364
|
+
}],
|
|
1365
|
+
returns: "disposer removing exactly the layer this call created; a no-op once the source has re-overridden (the newer layer is not torn down)."
|
|
1366
|
+
}
|
|
1367
|
+
]
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
key: "timer",
|
|
1371
|
+
summary: "Disposable timer helpers mixed into Cordis contexts.",
|
|
1372
|
+
description: "Disposable timer helpers mixed into Cordis contexts.",
|
|
1373
|
+
methods: [
|
|
1374
|
+
{
|
|
1375
|
+
signature: "timeout(callback: () => void, delay: number): () => void",
|
|
1376
|
+
description: "Run a callback once and return its disposer.",
|
|
1377
|
+
parameters: []
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
signature: "timeout(delay: number): Promise<void>",
|
|
1381
|
+
description: "Resolve after a delay; disposal rejects the pending promise.",
|
|
1382
|
+
parameters: []
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
signature: "interval(callback: () => void, delay: number): () => void",
|
|
1386
|
+
description: "Run a callback repeatedly and return its disposer.",
|
|
1387
|
+
parameters: []
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
signature: "interval<R = any>(delay: number): AsyncIterableIterator<void, R, void>",
|
|
1391
|
+
description: "Return an async iterator of timer ticks.",
|
|
1392
|
+
parameters: []
|
|
1393
|
+
},
|
|
1394
|
+
{
|
|
1395
|
+
signature: "throttle<F extends (...args: any[]) => void>(callback: F, delay: number, noTrailing?: boolean): F & { dispose: () => void }",
|
|
1396
|
+
description: "Return a throttled function whose timer is disposed with the current fiber.",
|
|
1397
|
+
parameters: []
|
|
1398
|
+
},
|
|
1399
|
+
{
|
|
1400
|
+
signature: "debounce<F extends (...args: any[]) => void>(callback: F, delay: number): F & { dispose: () => void }",
|
|
1401
|
+
description: "Return a debounced function whose timer is disposed with the current fiber.",
|
|
1402
|
+
parameters: []
|
|
1403
|
+
}
|
|
1404
|
+
]
|
|
1405
|
+
},
|
|
1406
|
+
{
|
|
1407
|
+
key: "workspaces",
|
|
1408
|
+
summary: "The workspaces-service face injected as `ctx.workspaces`.",
|
|
1409
|
+
description: "The workspaces-service face injected as `ctx.workspaces`.",
|
|
1410
|
+
methods: [
|
|
1411
|
+
{
|
|
1412
|
+
signature: "connectWorkspace(workspaceId: WorkspaceId): Promise<SessionId>",
|
|
1413
|
+
description: "Connect a Workspace to its reusable or freshly created blank session.",
|
|
1414
|
+
parameters: [{
|
|
1415
|
+
name: "workspaceId",
|
|
1416
|
+
description: "target workspace."
|
|
1417
|
+
}],
|
|
1418
|
+
returns: "the connected session id."
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
signature: "startSession(workspaceId?: WorkspaceId): void",
|
|
1422
|
+
description: "The New Session flow: connect the explicit, current-Session, or recent Workspace and open the resulting session; failures surface on the session list state.",
|
|
1423
|
+
parameters: [{
|
|
1424
|
+
name: "workspaceId",
|
|
1425
|
+
description: "explicit target; omitted inherits the current Session's Workspace before falling back to the recency projection."
|
|
1426
|
+
}]
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
signature: "create(input: { path: string }): Promise<WorkspaceView>",
|
|
1430
|
+
description: "Register an existing path as a Workspace.",
|
|
1431
|
+
parameters: [{
|
|
1432
|
+
name: "input",
|
|
1433
|
+
description: "the Host create payload."
|
|
1434
|
+
}],
|
|
1435
|
+
returns: "the created or idempotently resolved Workspace."
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
signature: "pickDirectory(): Promise<string | null>",
|
|
1439
|
+
description: "Open the Host's native directory picker.",
|
|
1440
|
+
parameters: [],
|
|
1441
|
+
returns: "the selected path, or null when the user cancelled."
|
|
1442
|
+
},
|
|
1443
|
+
{
|
|
1444
|
+
signature: "listDirectory(path?: string, signal?: AbortSignal): Promise<DirectoryListing>",
|
|
1445
|
+
description: "List one directory level through the Host's `browse` capability.",
|
|
1446
|
+
parameters: [{
|
|
1447
|
+
name: "path",
|
|
1448
|
+
description: "absolute directory to list; absent lists the Host home directory."
|
|
1449
|
+
}, {
|
|
1450
|
+
name: "signal",
|
|
1451
|
+
description: "aborts the wire request (and the Host's scan) when the caller supersedes it."
|
|
1452
|
+
}],
|
|
1453
|
+
returns: "the level's listing with breadcrumb ancestry."
|
|
1454
|
+
},
|
|
1455
|
+
{
|
|
1456
|
+
signature: "createDirectory(path: string, name: string): Promise<string>",
|
|
1457
|
+
description: "Create one child directory through the Host's `browse` capability.",
|
|
1458
|
+
parameters: [{
|
|
1459
|
+
name: "path",
|
|
1460
|
+
description: "absolute existing parent directory."
|
|
1461
|
+
}, {
|
|
1462
|
+
name: "name",
|
|
1463
|
+
description: "single non-blank path segment."
|
|
1464
|
+
}],
|
|
1465
|
+
returns: "the created directory's absolute path."
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
signature: "openPath(path: string): Promise<void>",
|
|
1469
|
+
description: "Open a filesystem path with the Host operating system's default application.",
|
|
1470
|
+
parameters: [{
|
|
1471
|
+
name: "path",
|
|
1472
|
+
description: "absolute or host-resolvable path."
|
|
1473
|
+
}]
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
signature: "rename(workspaceId: WorkspaceId, title: string): Promise<WorkspaceView>",
|
|
1477
|
+
description: "Rename a Workspace.",
|
|
1478
|
+
parameters: [{
|
|
1479
|
+
name: "workspaceId",
|
|
1480
|
+
description: "target workspace."
|
|
1481
|
+
}, {
|
|
1482
|
+
name: "title",
|
|
1483
|
+
description: "the new display title."
|
|
1484
|
+
}],
|
|
1485
|
+
returns: "the updated Workspace view."
|
|
1486
|
+
},
|
|
1487
|
+
{
|
|
1488
|
+
signature: "delete(workspaceId: WorkspaceId): Promise<void>",
|
|
1489
|
+
description: "Delete a Workspace (its sessions fall back to the unaccounted group).",
|
|
1490
|
+
parameters: [{
|
|
1491
|
+
name: "workspaceId",
|
|
1492
|
+
description: "target workspace."
|
|
1493
|
+
}]
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
signature: "insertSessionBefore(workspaceId: WorkspaceId, sessionId: SessionId, beforeSessionId?: SessionId): Promise<WorkspaceView>",
|
|
1497
|
+
description: "Move an accounted session within/into a Workspace's ordered list.",
|
|
1498
|
+
parameters: [
|
|
1499
|
+
{
|
|
1500
|
+
name: "workspaceId",
|
|
1501
|
+
description: "target workspace."
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
name: "sessionId",
|
|
1505
|
+
description: "accounted session to move."
|
|
1506
|
+
},
|
|
1507
|
+
{
|
|
1508
|
+
name: "beforeSessionId",
|
|
1509
|
+
description: "accounted anchor to insert before; omitted appends."
|
|
1510
|
+
}
|
|
1511
|
+
],
|
|
1512
|
+
returns: "the updated Workspace view."
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
signature: "archiveSession(sessionId: SessionId): Promise<void>",
|
|
1516
|
+
description: "Archive a session into the registry-global set (hidden from grouping surfaces; session log and accounting slot remain). Archiving the current session clears the selection into the New Session view state.",
|
|
1517
|
+
parameters: [{
|
|
1518
|
+
name: "sessionId",
|
|
1519
|
+
description: "session to archive."
|
|
1520
|
+
}]
|
|
1521
|
+
}
|
|
1522
|
+
]
|
|
1523
|
+
}
|
|
1524
|
+
];
|
|
1525
|
+
/** Every harness event, sorted by name. */
|
|
1526
|
+
const EVENT_API = [
|
|
1527
|
+
{
|
|
1528
|
+
name: "connection/reset",
|
|
1529
|
+
mode: "emit",
|
|
1530
|
+
signature: "'connection/reset'(): void",
|
|
1531
|
+
summary: "A connection generation was (re-)established.",
|
|
1532
|
+
description: "A connection generation was (re-)established. Wire-derived caches must treat their state as stale and repull (commands directory; the queue mirrors reset themselves through the session resync path).",
|
|
1533
|
+
parameters: []
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
name: "locale/change",
|
|
1537
|
+
mode: "emit",
|
|
1538
|
+
signature: "'locale/change'(snapshot: LocaleSnapshot): void",
|
|
1539
|
+
summary: "The active locale switched.",
|
|
1540
|
+
description: "The active locale switched. Dictionary registrations do NOT emit this event (listeners may re-register slots in response, and boot registers one namespace per package); continuous render refresh rides the LocaleFace revision instead.",
|
|
1541
|
+
parameters: [{
|
|
1542
|
+
name: "snapshot",
|
|
1543
|
+
description: "Current immutable locale snapshot."
|
|
1544
|
+
}]
|
|
1545
|
+
},
|
|
1546
|
+
{
|
|
1547
|
+
name: "slots/changed",
|
|
1548
|
+
mode: "emit",
|
|
1549
|
+
signature: "'slots/changed'(key: string): void",
|
|
1550
|
+
summary: "A slot's definition or registration set changed.",
|
|
1551
|
+
description: "A slot's definition or registration set changed.",
|
|
1552
|
+
parameters: [{
|
|
1553
|
+
name: "key",
|
|
1554
|
+
description: "the mutated SlotMap key."
|
|
1555
|
+
}]
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
name: "theme/change",
|
|
1559
|
+
mode: "emit",
|
|
1560
|
+
signature: "'theme/change'(snapshot: ThemeSnapshot): void",
|
|
1561
|
+
summary: "Theme state changed (preference switched, registry updated, or the OS color scheme changed while the preference is `system`).",
|
|
1562
|
+
description: "Theme state changed (preference switched, registry updated, or the OS color scheme changed while the preference is `system`).",
|
|
1563
|
+
parameters: [{
|
|
1564
|
+
name: "snapshot",
|
|
1565
|
+
description: "Current immutable theme snapshot."
|
|
1566
|
+
}]
|
|
1567
|
+
}
|
|
1568
|
+
];
|
|
1569
|
+
/** Shapes of every exported type the Service and Event signatures reference (transitively), sorted by name. */
|
|
1570
|
+
const TYPE_API = [
|
|
1571
|
+
{
|
|
1572
|
+
name: "ActionsDecl",
|
|
1573
|
+
declaration: "export type ActionsDecl<T> = Record<string, (draft: T, ...params: any[]) => void>;"
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
name: "AgentContext",
|
|
1577
|
+
declaration: "export type AgentContext = Omit<Context, 'remote'> & {\n readonly remote: TypertClientRemote & TypertRemoteScopeApi<'agent'>;\n};"
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
name: "AssistantBlock",
|
|
1581
|
+
declaration: "export type AssistantBlock = {\n kind: 'text';\n text: string;\n} | {\n kind: 'reasoning';\n text: string;\n} | {\n kind: 'image';\n attachment: ImageAttachmentRef;\n} | {\n kind: 'tool-call';\n callId: string;\n name: string;\n argsRaw: string;\n} | {\n kind: 'other';\n block: unknown;\n};"
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
name: "AssistantMessageNode",
|
|
1585
|
+
declaration: "export interface AssistantMessageNode {\n kind: 'assistant';\n seq: number;\n messageId?: MessageId;\n time: number;\n turn: number;\n step: number;\n blocks: readonly AssistantBlock[];\n usage?: unknown;\n provenance?: AssistantProvenanceView;\n requestConfig?: AssistantRequestConfig;\n timing?: AssistantTiming;\n interrupted?: true;\n}"
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
name: "AssistantProvenanceView",
|
|
1589
|
+
declaration: "export interface AssistantProvenanceView {\n provider: string;\n model: string;\n}"
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
name: "AssistantRequestConfig",
|
|
1593
|
+
declaration: "export interface AssistantRequestConfig {\n provider: string;\n model: string;\n purpose?: string;\n thinking?: string;\n reasoningEffort?: string;\n temperature?: number;\n maxTokens?: number;\n stop?: readonly string[];\n}"
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
name: "AssistantTiming",
|
|
1597
|
+
declaration: "export interface AssistantTiming {\n stepStartTime: number | null;\n firstTokenTime: number | null;\n completedTime: number;\n}"
|
|
1598
|
+
},
|
|
1599
|
+
{
|
|
1600
|
+
name: "BakedActions",
|
|
1601
|
+
declaration: "export type BakedActions<T, A extends ActionsDecl<T>> = {\n [K in keyof A]: A[K] extends (draft: T, ...params: infer P) => void ? (...params: P) => void : never;\n};"
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
name: "BoundActions",
|
|
1605
|
+
declaration: "export type BoundActions<H> = H extends StoreHandle<infer T, infer A> ? BakedActions<T, A> : never;"
|
|
1606
|
+
},
|
|
1607
|
+
{
|
|
1608
|
+
name: "ChainKeysOf",
|
|
1609
|
+
declaration: "export type ChainKeysOf<S extends keyof SlotMap & string> = S extends unknown ? (SlotMap[S]['kind'] extends 'chain' ? S : never) : never;"
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
name: "ChainRenderOpts",
|
|
1613
|
+
declaration: "export interface ChainRenderOpts {\n fallback?: ReactNode;\n overlay?: boolean;\n}"
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
name: "ChatConversationViewNode",
|
|
1617
|
+
declaration: "export interface ChatConversationViewNode extends ConversationViewNode {\n readonly target: 'chat';\n readonly anchorSeq: number;\n readonly location: ConversationLocation;\n readonly visibility: 'visible' | 'hidden';\n}"
|
|
1618
|
+
},
|
|
1619
|
+
{
|
|
1620
|
+
name: "ChatLocationNodeIndex",
|
|
1621
|
+
declaration: "export interface ChatLocationNodeIndex {\n getTurn(turn: number): readonly string[];\n getStep(turn: number, step: number): readonly string[];\n}"
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
name: "ChatNodeStore",
|
|
1625
|
+
declaration: "export interface ChatNodeStore {\n get(key: string): ChatConversationViewNode | undefined;\n values(): readonly ChatConversationViewNode[];\n}"
|
|
1626
|
+
},
|
|
1627
|
+
{
|
|
1628
|
+
name: "ChatSnapshot",
|
|
1629
|
+
declaration: "export interface ChatSnapshot {\n readonly order: readonly string[];\n readonly nodes: ChatNodeStore;\n readonly locations: ChatLocationNodeIndex;\n readonly timeline: ConversationTimelineSnapshot;\n readonly legacy: LegacyConversationSlice;\n}"
|
|
1630
|
+
},
|
|
1631
|
+
{
|
|
1632
|
+
name: "ChildrenDecl",
|
|
1633
|
+
declaration: "export type ChildrenDecl = {\n [P in keyof SlotMap & string]?: SlotSpec<SlotMap[P]>;\n};"
|
|
1634
|
+
},
|
|
1635
|
+
{
|
|
1636
|
+
name: "CommandNode",
|
|
1637
|
+
declaration: "export interface CommandNode {\n kind: 'command';\n seq: number;\n time: number;\n commandId: CommandId;\n name: string | null;\n args: string | null;\n outcome: {\n kind: 'success' | 'error';\n text?: string;\n sourceEventSeq?: number;\n } | null;\n}"
|
|
1638
|
+
},
|
|
1639
|
+
{
|
|
1640
|
+
name: "CommonKeyOf",
|
|
1641
|
+
declaration: "export type CommonKeyOf = LocaleNamespaceMap extends {\n common: infer C;\n} ? C & string : never;"
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
name: "CompactionSummaryNode",
|
|
1645
|
+
declaration: "export interface CompactionSummaryNode {\n kind: 'compaction';\n seq: number;\n time: number;\n summary: string | null;\n summaryEventSeq: number | null;\n shadowedItemCount: number | null;\n shadowedTokenCount: number | null;\n}"
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
name: "ComposedProps",
|
|
1649
|
+
declaration: "export type ComposedProps<K extends keyof SlotMap & string, EntryKey extends EntryKeyOf<K>, S extends keyof SlotMap & string, H, I extends object, M = never, N = undefined> = PropsRuntime<K, EntryKey> & PropsRenderSlots<S> & PropsStore<H> & InjectFace<I> & MatchedShare<SlotMap[K], M> & PropsLocale<N>;"
|
|
1650
|
+
},
|
|
1651
|
+
{
|
|
1652
|
+
name: "ComposerPhase",
|
|
1653
|
+
declaration: "export type ComposerPhase = 'blank' | 'engaging' | 'active';"
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
name: "ContextMessageNode",
|
|
1657
|
+
declaration: "export interface ContextMessageNode {\n kind: 'context';\n seq: number;\n time: number;\n content: readonly ContentBlock[];\n source: unknown;\n provenance: ContextProvenanceView;\n form: KnownContextForm | null;\n}"
|
|
1658
|
+
},
|
|
1659
|
+
{
|
|
1660
|
+
name: "ContextProvenanceView",
|
|
1661
|
+
declaration: "export interface ContextProvenanceView {\n role: ContextRole;\n label: string | null;\n}"
|
|
1662
|
+
},
|
|
1663
|
+
{
|
|
1664
|
+
name: "ContextRole",
|
|
1665
|
+
declaration: "export type ContextRole = 'inject' | 'recall';"
|
|
1666
|
+
},
|
|
1667
|
+
{
|
|
1668
|
+
name: "ConversationLocation",
|
|
1669
|
+
declaration: "export type ConversationLocation = {\n readonly kind: 'session';\n} | {\n readonly kind: 'turn';\n readonly turn: TurnLocation;\n} | {\n readonly kind: 'step';\n readonly turn: TurnLocation;\n readonly step: StepLocation;\n} | {\n readonly kind: 'unresolved';\n};"
|
|
1670
|
+
},
|
|
1671
|
+
{
|
|
1672
|
+
name: "ConversationLocationDataStore",
|
|
1673
|
+
declaration: "export interface ConversationLocationDataStore<DataMap extends object> {\n get<Key extends keyof DataMap & string>(key: Key): Readonly<DataMap[Key]> | undefined;\n}"
|
|
1674
|
+
},
|
|
1675
|
+
{
|
|
1676
|
+
name: "ConversationNode",
|
|
1677
|
+
declaration: "export type ConversationNode = UserMessageNode | AssistantMessageNode | SteeringMessageNode | ContextMessageNode | ModelRetryNode | TurnErrorNode | TurnMaxTokensNode | ToolResultNode | CommandNode | CompactionSummaryNode | UnknownSurfaceNode;"
|
|
1678
|
+
},
|
|
1679
|
+
{
|
|
1680
|
+
name: "ConversationSnapshot",
|
|
1681
|
+
declaration: "export interface ConversationSnapshot {\n sessionId: SessionId;\n views: ConversationViewSnapshotStore;\n chat: ChatSnapshot;\n nodes: readonly ConversationNode[];\n turnTimings: ReadonlyMap<number, {\n readonly startTime: number;\n readonly endTime?: number;\n }>;\n turnEnds: ReadonlyMap<number, number>;\n partial: PartialAssistant | null;\n runningCalls: readonly RunningToolCall[];\n pending: readonly PendingInteraction[];\n queue: readonly QueuedMessage[];\n running: boolean;\n subagent: {\n address: SubagentAddress;\n parentAvailable: boolean;\n } | null;\n composerPhase: ComposerPhase;\n removed: boolean;\n openState: OpenState;\n openError: RpcError | null;\n hasMore: boolean;\n loadingOlder: boolean;\n promptError: PromptError | null;\n blank: boolean;\n lastAgentError: string | null;\n}"
|
|
1682
|
+
},
|
|
1683
|
+
{
|
|
1684
|
+
name: "ConversationStepDataMap",
|
|
1685
|
+
declaration: "export interface ConversationStepDataMap {\n}"
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
name: "ConversationTimelineSnapshot",
|
|
1689
|
+
declaration: "export interface ConversationTimelineSnapshot {\n readonly turnOrder: readonly number[];\n readonly turns: ReadonlyMap<number, TurnLocation>;\n}"
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
name: "ConversationTurnDataMap",
|
|
1693
|
+
declaration: "export interface ConversationTurnDataMap {\n}"
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
name: "ConversationViewNode",
|
|
1697
|
+
declaration: "export interface ConversationViewNode {\n readonly key: string;\n readonly kind: string;\n readonly id: string;\n readonly target: string;\n readonly data: unknown;\n}"
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
name: "ConversationViewSnapshotMap",
|
|
1701
|
+
declaration: "export interface ConversationViewSnapshotMap {\n}"
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
name: "ConversationViewSnapshotStore",
|
|
1705
|
+
declaration: "export interface ConversationViewSnapshotStore {\n get<Target extends Extract<keyof ConversationViewSnapshotMap, string>>(target: Target): ConversationViewSnapshotMap[Target] | undefined;\n}"
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
name: "EntryKeyOf",
|
|
1709
|
+
declaration: "export type EntryKeyOf<K extends keyof SlotMap & string> = SlotMap[K] extends {\n kind: 'keyed';\n keyProps: infer P extends object;\n} ? keyof P & string : string;"
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
name: "GlobalStandardProps",
|
|
1713
|
+
declaration: "export interface GlobalStandardProps {\n}"
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
name: "HandleOf",
|
|
1717
|
+
declaration: "export type HandleOf<H> = H extends () => infer R ? R : H;"
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
name: "HooksSources",
|
|
1721
|
+
declaration: "export type HooksSources = Record<string, HostObservable<unknown>>;"
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
name: "HostObservable",
|
|
1725
|
+
declaration: "export interface HostObservable<T> {\n getSnapshot(): T;\n subscribe(fn: () => void): () => void;\n}"
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
name: "InjectFace",
|
|
1729
|
+
declaration: "export type InjectFace<I extends object> = I extends {\n hooks: infer HS extends HooksSources;\n} ? Omit<I, 'hooks'> & PropsHooks<HS> : I;"
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
name: "InjectParams",
|
|
1733
|
+
declaration: "export type InjectParams<K extends keyof SlotMap & string, H> = ScopeOf<K> extends 'session' ? ([\n H\n] extends [\n StoreDecl\n] ? [\n sessionId: SessionIdOf,\n actions: BoundActions<HandleOf<H>>\n] : [\n sessionId: SessionIdOf\n]) : ScopeOf<K> extends 'session-maybe' ? ([\n H\n] extends [\n StoreDecl\n] ? [\n sessionId: SessionIdOf | undefined,\n actions: BoundActions<HandleOf<H>> | undefined\n] : [\n sessionId: SessionIdOf | undefined\n]) : ([\n H\n] extends [\n StoreDecl\n] ? [\n actions: BoundActions<HandleOf<H>>\n] : [\n]);"
|
|
1734
|
+
},
|
|
1735
|
+
{
|
|
1736
|
+
name: "ISession",
|
|
1737
|
+
declaration: "export interface ISession {\n readonly sessionId: SessionId;\n readonly projections: ProjectionsFace;\n prompt(content: PromptContentPart[], mode: 'queue' | 'steer', signal?: AbortSignal): Promise<RpcResult<{\n accepted: true;\n }>>;\n readAttachment(attachmentId: AttachmentIdType): Promise<RpcResult<{\n attachment: ImageAttachmentRef;\n data: Uint8Array;\n }>>;\n updateQueue(itemId: MessageId, action: QueueAction): Promise<RpcResult<{\n accepted: true;\n }>>;\n cancel(): Promise<RpcResult<{\n accepted: true;\n }>>;\n rename(title: string): Promise<RpcResult<{\n title: string;\n seq: number;\n }>>;\n loadOlder(): Promise<void>;\n command(line: string): Promise<RemoteResult<{\n matched: boolean;\n }>>;\n}"
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
name: "KeyPropsOf",
|
|
1741
|
+
declaration: "export type KeyPropsOf<K extends keyof SlotMap & string, EntryKey extends EntryKeyOf<K>> = SlotMap[K] extends {\n kind: 'keyed';\n keyProps: infer P extends object;\n} ? EntryKey extends keyof P ? P[EntryKey] extends object ? P[EntryKey] : never : never : object;"
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
name: "KnownContextForm",
|
|
1745
|
+
declaration: "export type KnownContextForm = typeof KNOWN_FORMS[number];"
|
|
1746
|
+
},
|
|
1747
|
+
{
|
|
1748
|
+
name: "LegacyConversationSlice",
|
|
1749
|
+
declaration: "export interface LegacyConversationSlice {\n readonly nodes: readonly ConversationNode[];\n readonly turnTimings: ReadonlyMap<number, {\n readonly startTime: number;\n readonly endTime?: number;\n }>;\n readonly turnEnds: ReadonlyMap<number, number>;\n readonly partial: PartialAssistant | null;\n readonly runningCalls: readonly RunningToolCall[];\n}"
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
name: "LocaleDefinition",
|
|
1753
|
+
declaration: "export interface LocaleDefinition {\n id: LocaleId;\n label: string;\n}"
|
|
1754
|
+
},
|
|
1755
|
+
{
|
|
1756
|
+
name: "LocaleDict",
|
|
1757
|
+
declaration: "export type LocaleDict = Record<string, string>;"
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
name: "LocaleDictOf",
|
|
1761
|
+
declaration: "export type LocaleDictOf<N extends keyof LocaleNamespaceMap & string> = Record<LocaleNamespaceMap[N] & string, string>;"
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
name: "LocaleId",
|
|
1765
|
+
declaration: "export type LocaleId = typeof LOCALE_IDS[number];"
|
|
1766
|
+
},
|
|
1767
|
+
{
|
|
1768
|
+
name: "LocaleKeysOf",
|
|
1769
|
+
declaration: "export type LocaleKeysOf<N extends keyof LocaleNamespaceMap & string> = (LocaleNamespaceMap[N] & string) | CommonKeyOf;"
|
|
1770
|
+
},
|
|
1771
|
+
{
|
|
1772
|
+
name: "LocaleNamespaceMap",
|
|
1773
|
+
declaration: "export interface LocaleNamespaceMap {\n}"
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
name: "LocaleSnapshot",
|
|
1777
|
+
declaration: "export interface LocaleSnapshot {\n active: LocaleId;\n locales: readonly LocaleDefinition[];\n revision: number;\n}"
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
name: "MatchedShare",
|
|
1781
|
+
declaration: "export type MatchedShare<E extends SlotEntryDef, M> = E['kind'] extends 'chain' ? {\n matched: M;\n} : object;"
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
name: "ModelRetryNode",
|
|
1785
|
+
declaration: "export type ModelRetryNode = LlmRetryEventData & {\n kind: 'model-retry';\n seq: number;\n time: number;\n retryState: 'scheduled' | 'started' | 'cancelled';\n};"
|
|
1786
|
+
},
|
|
1787
|
+
{
|
|
1788
|
+
name: "ObservableSnapshot",
|
|
1789
|
+
declaration: "export interface ObservableSnapshot<T> {\n getSnapshot(): T;\n subscribe(fn: () => void): () => void;\n}"
|
|
1790
|
+
},
|
|
1791
|
+
{
|
|
1792
|
+
name: "OpenState",
|
|
1793
|
+
declaration: "export type OpenState = 'cold' | 'loading' | 'open' | 'error';"
|
|
1794
|
+
},
|
|
1795
|
+
{
|
|
1796
|
+
name: "OwnerOf",
|
|
1797
|
+
declaration: "export type OwnerOf<K extends keyof SlotMap & string> = SlotMap[K] extends {\n owner: infer O extends object;\n} ? O : object;"
|
|
1798
|
+
},
|
|
1799
|
+
{
|
|
1800
|
+
name: "PartialAssistant",
|
|
1801
|
+
declaration: "export interface PartialAssistant {\n turn: number;\n step: number;\n blocks: readonly AssistantBlock[];\n}"
|
|
1802
|
+
},
|
|
1803
|
+
{
|
|
1804
|
+
name: "PendingInteraction",
|
|
1805
|
+
declaration: "export type PendingInteraction = {\n [K in PendingKind]: PendingWait<K>;\n}[PendingKind];"
|
|
1806
|
+
},
|
|
1807
|
+
{
|
|
1808
|
+
name: "PendingKind",
|
|
1809
|
+
declaration: "export type PendingKind = keyof PendingPayloads;"
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
name: "PendingPayloads",
|
|
1813
|
+
declaration: "export interface PendingPayloads {\n approval: Omit<Extract<MuxFrame, {\n type: 'approval/requested';\n }>, 'type' | 'sessionId'>;\n question: Omit<Extract<MuxFrame, {\n type: 'question/requested';\n }>, 'type' | 'sessionId'>;\n}"
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
name: "PendingWait",
|
|
1817
|
+
declaration: "export class PendingWait<K extends PendingKind = PendingKind> {\n readonly kind: K;\n readonly key: string;\n readonly sessionId: SessionId;\n readonly payload: PendingPayloads[K];\n constructor(kind: K, rpcId: RpcId, sessionId: SessionId, payload: PendingPayloads[K], respond: (message: ClientResponse) => Promise<RpcReceipt>);\n respond(result: ClientResponse['result']): Promise<RpcReceipt>;\n markSettled(): void;\n}"
|
|
1818
|
+
},
|
|
1819
|
+
{
|
|
1820
|
+
name: "ProjectionsFace",
|
|
1821
|
+
declaration: "export interface ProjectionsFace {\n faceOf(key: string): ObservableSnapshot<unknown>;\n}"
|
|
1822
|
+
},
|
|
1823
|
+
{
|
|
1824
|
+
name: "PromptError",
|
|
1825
|
+
declaration: "export interface PromptError {\n op: 'send' | 'stop';\n error: RpcError;\n}"
|
|
1826
|
+
},
|
|
1827
|
+
{
|
|
1828
|
+
name: "PropsHooks",
|
|
1829
|
+
declaration: "export type PropsHooks<HS extends HooksSources> = {\n [N in keyof HS & string as `use${Capitalize<N>}`]: SnapshotSelectorHook<HS[N] extends HostObservable<infer T> ? T : never>;\n};"
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
name: "PropsLocale",
|
|
1833
|
+
declaration: "export type PropsLocale<N> = N extends keyof LocaleNamespaceMap & string ? {\n t: TranslateNS<N>;\n} : object;"
|
|
1834
|
+
},
|
|
1835
|
+
{
|
|
1836
|
+
name: "PropsRenderSlots",
|
|
1837
|
+
declaration: "export type PropsRenderSlots<S extends keyof SlotMap & string> = {\n renderSlot: RenderSlotFn<Exclude<S, ChainKeysOf<S>>>;\n readonly __renders?: ((key: S) => void) | undefined;\n} & ([\n ChainKeysOf<S>\n] extends [\n never\n] ? object : {\n renderSlotChain: <K extends ChainKeysOf<S>>(key: K, owner: OwnerOf<K>, opts?: ChainRenderOpts) => ReactNode;\n}) & ('session' extends ScopeOf<S> ? {\n SessionProvider: SessionProviderComponent;\n} : object);"
|
|
1838
|
+
},
|
|
1839
|
+
{
|
|
1840
|
+
name: "PropsRuntime",
|
|
1841
|
+
declaration: "export type PropsRuntime<K extends keyof SlotMap & string, EntryKey extends EntryKeyOf<K> = EntryKeyOf<K>> = OwnerOf<K> & KeyPropsOf<K, EntryKey> & SlotInjectFace<SlotInjectOf<K>> & (ScopeOf<K> extends 'session' ? SessionStandardProps : ScopeOf<K> extends 'session-maybe' ? SessionMaybeStandardProps : object) & GlobalStandardProps;"
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
name: "PropsSlotHooks",
|
|
1845
|
+
declaration: "export type PropsSlotHooks<HS extends object> = {\n [N in keyof HS & string as `use${Capitalize<N>}`]: BoundHookOf<HS[N]>;\n};"
|
|
1846
|
+
},
|
|
1847
|
+
{
|
|
1848
|
+
name: "PropsStore",
|
|
1849
|
+
declaration: "export type PropsStore<H> = H extends StoreHandle<infer T, infer A> ? {\n useStore: SnapshotSelectorHook<T>;\n actions: BakedActions<T, A>;\n} : object;"
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
name: "QueueAction",
|
|
1853
|
+
declaration: "export type QueueAction = Parameters<SessionFace['updateQueue']>[1];"
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
name: "RunningToolCall",
|
|
1857
|
+
declaration: "export interface RunningToolCall {\n callId: string;\n name: string;\n argsRaw: string;\n turn: number;\n step: number;\n time: number;\n callView: ToolCallView | null;\n subCalls: readonly ToolCallBlock[];\n}"
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
name: "ScopeOf",
|
|
1861
|
+
declaration: "export type ScopeOf<K extends keyof SlotMap & string> = SlotMap[K]['scope'];"
|
|
1862
|
+
},
|
|
1863
|
+
{
|
|
1864
|
+
name: "SessionAreaProps",
|
|
1865
|
+
declaration: "export interface SessionAreaProps {\n empty?: (() => ReactNode) | undefined;\n children: (sessionId: SessionIdOf) => ReactNode;\n}"
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
name: "SessionBinding",
|
|
1869
|
+
declaration: "export interface SessionBinding {\n readonly sessionId: SessionId;\n readonly session: SessionFace;\n readonly ctx: AgentContext;\n}"
|
|
1870
|
+
},
|
|
1871
|
+
{
|
|
1872
|
+
name: "SessionFace",
|
|
1873
|
+
declaration: "export type SessionFace = ISession & ObservableSnapshot<ConversationSnapshot>;"
|
|
1874
|
+
},
|
|
1875
|
+
{
|
|
1876
|
+
name: "SessionIdOf",
|
|
1877
|
+
declaration: "export type SessionIdOf = SessionStandardProps extends {\n sessionId: infer S;\n} ? S : string;"
|
|
1878
|
+
},
|
|
1879
|
+
{
|
|
1880
|
+
name: "SessionMaybeStandardProps",
|
|
1881
|
+
declaration: "export interface SessionMaybeStandardProps {\n}"
|
|
1882
|
+
},
|
|
1883
|
+
{
|
|
1884
|
+
name: "SessionProviderComponent",
|
|
1885
|
+
declaration: "export type SessionProviderComponent = (props: SessionAreaProps) => ReactNode;"
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
name: "SessionSearchResultItem",
|
|
1889
|
+
declaration: "export interface SessionSearchResultItem {\n sessionId: SessionId;\n snippet: string;\n}"
|
|
1890
|
+
},
|
|
1891
|
+
{
|
|
1892
|
+
name: "SessionStandardProps",
|
|
1893
|
+
declaration: "export interface SessionStandardProps {\n}"
|
|
1894
|
+
},
|
|
1895
|
+
{
|
|
1896
|
+
name: "SlotComponent",
|
|
1897
|
+
declaration: "export type SlotComponent<P> = (props: P) => ReactNode;"
|
|
1898
|
+
},
|
|
1899
|
+
{
|
|
1900
|
+
name: "SlotCore",
|
|
1901
|
+
declaration: "export class SlotCore {\n constructor();\n register<K extends keyof SlotMap & string, const EntryKey extends EntryKeyOf<K> = EntryKeyOf<K>, const D extends ChildrenDecl = Record<never, never>, H extends StoreDecl | undefined = undefined, M = never, N extends (keyof LocaleNamespaceMap & string) | undefined = undefined, C extends SlotComponent<never> = SlotComponent<never>>(options: BaseOptions<K, EntryKey, D, H, M, N> & {\n inject?: undefined;\n }, component: C & SlotComponent<ComposedProps<K, NoInfer<EntryKey>, keyof NoInfer<D> & keyof SlotMap & string, HandleOf<NoInfer<H>>, object, NoInfer<M>, NoInfer<N>>> & RendersCheck<C, D>): () => void;\n register<K extends keyof SlotMap & string, I extends object, const EntryKey extends EntryKeyOf<K> = EntryKeyOf<K>, const D extends ChildrenDecl = Record<never, never>, H extends StoreDecl | undefined = undefined, M = never, N extends (keyof LocaleNamespaceMap & string) | undefined = undefined, C extends SlotComponent<never> = SlotComponent<never>>(options: BaseOptions<K, EntryKey, D, H, M, N> & {\n inject: (...args: InjectParams<K, H>) => I;\n }, component: C & SlotComponent<ComposedProps<K, NoInfer<EntryKey>, keyof NoInfer<D> & keyof SlotMap & string, HandleOf<NoInfer<H>>, I, NoInfer<M>, NoInfer<N>>> & RendersCheck<C, D>): () => void;\n register(options: ErasedOptions, component: unknown): () => void;\n isLive(entry: StoredEntry): boolean;\n entries(key: string): readonly StoredEntry[];\n entriesOfSlot(key /* …truncated — full shape in source */"
|
|
1902
|
+
},
|
|
1903
|
+
{
|
|
1904
|
+
name: "SlotEntryDef",
|
|
1905
|
+
declaration: "export interface SlotEntryDef {\n kind: SlotKind;\n scope: SlotScope;\n owner?: object;\n keyProps?: Record<string, object>;\n hookContext?: unknown;\n inject?: object;\n}"
|
|
1906
|
+
},
|
|
1907
|
+
{
|
|
1908
|
+
name: "SlotInjectFace",
|
|
1909
|
+
declaration: "export type SlotInjectFace<I extends object> = I extends {\n hooks: infer HS extends object;\n} ? Omit<I, 'hooks'> & PropsSlotHooks<HS> : I;"
|
|
1910
|
+
},
|
|
1911
|
+
{
|
|
1912
|
+
name: "SlotInjectOf",
|
|
1913
|
+
declaration: "export type SlotInjectOf<K extends keyof SlotMap & string> = SlotMap[K] extends {\n inject: infer Injected extends object;\n} ? Injected : object;"
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
name: "SlotKind",
|
|
1917
|
+
declaration: "export type SlotKind = 'single' | 'list' | 'keyed' | 'chain';"
|
|
1918
|
+
},
|
|
1919
|
+
{
|
|
1920
|
+
name: "SlotLabel",
|
|
1921
|
+
declaration: "export type SlotLabel = string | (() => string);"
|
|
1922
|
+
},
|
|
1923
|
+
{
|
|
1924
|
+
name: "SlotMap",
|
|
1925
|
+
declaration: "export interface SlotMap {\n}"
|
|
1926
|
+
},
|
|
1927
|
+
{
|
|
1928
|
+
name: "SlotScope",
|
|
1929
|
+
declaration: "export type SlotScope = 'root' | 'session-maybe' | 'session';"
|
|
1930
|
+
},
|
|
1931
|
+
{
|
|
1932
|
+
name: "SlotSpec",
|
|
1933
|
+
declaration: "export type SlotSpec<E extends SlotEntryDef> = {\n kind: E['kind'];\n scope: E['scope'];\n} & ('inject' extends keyof E ? E extends {\n inject: infer Injected extends object;\n} ? {\n inject: Injected;\n} : {\n inject?: object;\n} : {\n inject?: never;\n});"
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
name: "SnapshotSelectorHook",
|
|
1937
|
+
declaration: "export type SnapshotSelectorHook<T> = <S>(sel: (s: T) => S, eq?: (a: S, b: S) => boolean) => S;"
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
name: "SteeringMessageNode",
|
|
1941
|
+
declaration: "export interface SteeringMessageNode {\n kind: 'steering';\n messageId: MessageId;\n seq: number;\n time: number;\n content: readonly ContentBlock[];\n source: unknown;\n}"
|
|
1942
|
+
},
|
|
1943
|
+
{
|
|
1944
|
+
name: "StepLocation",
|
|
1945
|
+
declaration: "export interface StepLocation {\n readonly turn: number;\n readonly step: number;\n readonly start: SessionEvent<'step/start'> | undefined;\n readonly end: SessionEvent<'step/end'> | undefined;\n readonly status: 'open' | 'closed' | 'unknown';\n readonly data: ConversationLocationDataStore<ConversationStepDataMap>;\n}"
|
|
1946
|
+
},
|
|
1947
|
+
{
|
|
1948
|
+
name: "StoreDecl",
|
|
1949
|
+
declaration: "export type StoreDecl = StoreHandle<any, any> | StoreFactory;"
|
|
1950
|
+
},
|
|
1951
|
+
{
|
|
1952
|
+
name: "StoredEntry",
|
|
1953
|
+
declaration: "export interface StoredEntry {\n component: unknown;\n options: {\n key?: string;\n id?: string;\n order?: number;\n label?: SlotLabel;\n priority?: number;\n };\n select?: ((owner: never) => unknown) | undefined;\n inject?: ((...args: never[]) => Record<string, unknown>) | undefined;\n children?: Readonly<Record<string, SlotSpec<SlotEntryDef>>> | undefined;\n store?: StoreDecl | undefined;\n locale?: string | undefined;\n registrant?: string | undefined;\n}"
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
name: "StoreFactory",
|
|
1957
|
+
declaration: "export type StoreFactory = () => StoreHandle<any, any>;"
|
|
1958
|
+
},
|
|
1959
|
+
{
|
|
1960
|
+
name: "StoreHandle",
|
|
1961
|
+
declaration: "export interface StoreHandle<T, A extends ActionsDecl<T>> {\n readonly spec: StoreSpec<T, A>;\n create(scopeKey?: string): StoreInstance<T, A>;\n}"
|
|
1962
|
+
},
|
|
1963
|
+
{
|
|
1964
|
+
name: "StoreInstance",
|
|
1965
|
+
declaration: "export interface StoreInstance<T, A extends ActionsDecl<T>> {\n readonly actions: BakedActions<T, A>;\n getSnapshot(): T;\n subscribe(fn: () => void): () => void;\n clearPersisted(): void;\n}"
|
|
1966
|
+
},
|
|
1967
|
+
{
|
|
1968
|
+
name: "StoreSpec",
|
|
1969
|
+
declaration: "export interface StoreSpec<T, A extends ActionsDecl<T>> {\n init: () => T;\n persist?: string;\n actions: A;\n}"
|
|
1970
|
+
},
|
|
1971
|
+
{
|
|
1972
|
+
name: "ThemeDefinition",
|
|
1973
|
+
declaration: "export interface ThemeDefinition {\n id: string;\n colorScheme: 'light' | 'dark';\n tokens: ThemeTokens;\n}"
|
|
1974
|
+
},
|
|
1975
|
+
{
|
|
1976
|
+
name: "ThemePreference",
|
|
1977
|
+
declaration: "export type ThemePreference = typeof THEME_PREFERENCES[number];"
|
|
1978
|
+
},
|
|
1979
|
+
{
|
|
1980
|
+
name: "ThemeSnapshot",
|
|
1981
|
+
declaration: "export interface ThemeSnapshot {\n preference: ThemePreference;\n active: ThemeDefinition;\n themes: readonly ThemeDefinition[];\n revision: number;\n}"
|
|
1982
|
+
},
|
|
1983
|
+
{
|
|
1984
|
+
name: "ThemeTokenModes",
|
|
1985
|
+
declaration: "export interface ThemeTokenModes {\n light: string;\n dark: string;\n}"
|
|
1986
|
+
},
|
|
1987
|
+
{
|
|
1988
|
+
name: "ThemeTokenOverrides",
|
|
1989
|
+
declaration: "export type ThemeTokenOverrides = Record<string, ThemeTokenModes>;"
|
|
1990
|
+
},
|
|
1991
|
+
{
|
|
1992
|
+
name: "ThemeTokens",
|
|
1993
|
+
declaration: "export type ThemeTokens = Record<string, string>;"
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
name: "ToolCallBlock",
|
|
1997
|
+
declaration: "export type ToolCallBlock = RunningToolCall | ToolResultNode;"
|
|
1998
|
+
},
|
|
1999
|
+
{
|
|
2000
|
+
name: "ToolResultNode",
|
|
2001
|
+
declaration: "export interface ToolResultNode {\n kind: 'tool-result';\n seq: number;\n time: number;\n callId: string;\n call: {\n name: string;\n argsRaw: string;\n } | null;\n callTime: number | null;\n content: readonly ContentBlock[];\n isError: boolean;\n error?: {\n name: string;\n code: string;\n };\n meta?: unknown;\n callView: ToolCallView | null;\n resultView: ToolResultView | null;\n subCalls: readonly ToolCallBlock[];\n}"
|
|
2002
|
+
},
|
|
2003
|
+
{
|
|
2004
|
+
name: "Translate",
|
|
2005
|
+
declaration: "export type Translate<K extends string = string> = (key: K, params?: Record<string, unknown>) => string;"
|
|
2006
|
+
},
|
|
2007
|
+
{
|
|
2008
|
+
name: "TranslateNS",
|
|
2009
|
+
declaration: "export type TranslateNS<N extends keyof LocaleNamespaceMap & string> = Translate<LocaleKeysOf<N>>;"
|
|
2010
|
+
},
|
|
2011
|
+
{
|
|
2012
|
+
name: "TurnErrorNode",
|
|
2013
|
+
declaration: "export interface TurnErrorNode {\n kind: 'turn-error';\n seq: number;\n time: number;\n turn: number;\n step: number;\n message: string;\n code?: string;\n}"
|
|
2014
|
+
},
|
|
2015
|
+
{
|
|
2016
|
+
name: "TurnLocation",
|
|
2017
|
+
declaration: "export interface TurnLocation {\n readonly turn: number;\n readonly start: SessionEvent<'turn/start'> | undefined;\n readonly end: SessionEvent<'turn/end'> | undefined;\n readonly status: 'open' | 'closed' | 'unknown';\n readonly steps: readonly StepLocation[];\n readonly data: ConversationLocationDataStore<ConversationTurnDataMap>;\n}"
|
|
2018
|
+
},
|
|
2019
|
+
{
|
|
2020
|
+
name: "TurnMaxTokensNode",
|
|
2021
|
+
declaration: "export interface TurnMaxTokensNode {\n kind: 'turn-max-tokens';\n seq: number;\n time: number;\n turn: number;\n step: number;\n}"
|
|
2022
|
+
},
|
|
2023
|
+
{
|
|
2024
|
+
name: "UnknownSurfaceNode",
|
|
2025
|
+
declaration: "export interface UnknownSurfaceNode {\n kind: 'unknown';\n seq: number;\n time: number;\n type: string;\n data: unknown;\n}"
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
name: "UserMessageNode",
|
|
2029
|
+
declaration: "export interface UserMessageNode {\n kind: 'user';\n seq: number;\n time: number;\n content: readonly ContentBlock[];\n source: unknown;\n}"
|
|
2030
|
+
}
|
|
2031
|
+
];
|
|
2032
|
+
function referencedTypeClosure(seeds) {
|
|
2033
|
+
const included = /* @__PURE__ */ new Set();
|
|
2034
|
+
let frontier = [...seeds];
|
|
2035
|
+
while (frontier.length > 0) {
|
|
2036
|
+
const next = [];
|
|
2037
|
+
for (const entry of TYPE_API) {
|
|
2038
|
+
if (included.has(entry.name)) continue;
|
|
2039
|
+
const pattern = new RegExp(`\b${entry.name}\b`);
|
|
2040
|
+
if (!frontier.some((text) => pattern.test(text))) continue;
|
|
2041
|
+
included.add(entry.name);
|
|
2042
|
+
next.push(entry.declaration);
|
|
2043
|
+
}
|
|
2044
|
+
frontier = next;
|
|
2045
|
+
}
|
|
2046
|
+
return TYPE_API.filter((entry) => included.has(entry.name));
|
|
2047
|
+
}
|
|
2048
|
+
function contextProperty(key) {
|
|
2049
|
+
return /^[A-Za-z_$][\w$]*$/.test(key) ? `ctx.${key}` : `ctx[${JSON.stringify(key)}]`;
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* Project the Service Catalog as a compact directory or one exact coding contract.
|
|
2053
|
+
* @param key - exact Service key; omit it to list all Services and method signatures.
|
|
2054
|
+
* @param services - platform-specific visible Service entries.
|
|
2055
|
+
* @returns compact navigation data or one detailed Service with its referenced type closure.
|
|
2056
|
+
*/
|
|
2057
|
+
function queryServiceApi(key, services = SERVICE_API) {
|
|
2058
|
+
if (key === void 0) return {
|
|
2059
|
+
mode: "catalog",
|
|
2060
|
+
services: services.map((service) => ({
|
|
2061
|
+
key: service.key,
|
|
2062
|
+
description: service.summary,
|
|
2063
|
+
methods: service.methods.map((method) => ({ signature: method.signature }))
|
|
2064
|
+
}))
|
|
2065
|
+
};
|
|
2066
|
+
const service = services.find((candidate) => candidate.key === key);
|
|
2067
|
+
if (service === void 0) throw new Error(`no catalogued Service named "${key}"`);
|
|
2068
|
+
return {
|
|
2069
|
+
mode: "service",
|
|
2070
|
+
service: {
|
|
2071
|
+
key: service.key,
|
|
2072
|
+
description: service.description,
|
|
2073
|
+
access: {
|
|
2074
|
+
optional: {
|
|
2075
|
+
expression: `ctx.get(${JSON.stringify(service.key)})`,
|
|
2076
|
+
requiresUndefinedCheck: true
|
|
2077
|
+
},
|
|
2078
|
+
hardDependency: {
|
|
2079
|
+
inject: [service.key],
|
|
2080
|
+
expression: contextProperty(service.key)
|
|
2081
|
+
}
|
|
2082
|
+
},
|
|
2083
|
+
methods: service.methods
|
|
2084
|
+
},
|
|
2085
|
+
referencedTypes: referencedTypeClosure(service.methods.map((method) => method.signature))
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Project the Event Catalog as a compact directory or one exact listener contract.
|
|
2090
|
+
* @param name - exact Event name; omit it to list all Events and listener signatures.
|
|
2091
|
+
* @param events - platform-specific visible Event entries.
|
|
2092
|
+
* @returns compact navigation data or one detailed Event with its referenced type closure.
|
|
2093
|
+
*/
|
|
2094
|
+
function queryEventApi(name, events = EVENT_API) {
|
|
2095
|
+
if (name === void 0) return {
|
|
2096
|
+
mode: "catalog",
|
|
2097
|
+
events: events.map((event) => ({
|
|
2098
|
+
name: event.name,
|
|
2099
|
+
description: event.summary,
|
|
2100
|
+
mode: event.mode,
|
|
2101
|
+
signature: event.signature
|
|
2102
|
+
}))
|
|
2103
|
+
};
|
|
2104
|
+
const event = events.find((candidate) => candidate.name === name);
|
|
2105
|
+
if (event === void 0) throw new Error(`no catalogued Event named "${name}"`);
|
|
2106
|
+
return {
|
|
2107
|
+
mode: "event",
|
|
2108
|
+
event: {
|
|
2109
|
+
name: event.name,
|
|
2110
|
+
description: event.description,
|
|
2111
|
+
mode: event.mode,
|
|
2112
|
+
signature: event.signature,
|
|
2113
|
+
parameters: event.parameters
|
|
2114
|
+
},
|
|
2115
|
+
referencedTypes: referencedTypeClosure([event.signature])
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
//#endregion
|
|
2119
|
+
//#region lib/types/client/slot-catalog.js
|
|
2120
|
+
/** Every slot the shipped web bundle declares, sorted by key. */
|
|
2121
|
+
const CLIENT_SLOT_API = [
|
|
2122
|
+
{
|
|
2123
|
+
key: "conversation",
|
|
2124
|
+
kind: "single",
|
|
2125
|
+
scope: "session-maybe",
|
|
2126
|
+
summary: "The whole center column, across both the no-session hero and a live conversation.",
|
|
2127
|
+
doc: "The whole center column, across both the no-session hero and a live\nconversation. OCCUPIED by ui-conversation's ConversationRoot, which\ndeclares the session body, composer, and input seats inside it —\nregistering here replaces the entire conversation surface (and removes\nevery seat it declares) rather than adding to it.\n\nCurrent-session-optional: the occupant owns both states without\nchanging its React identity, so it keeps its own state across a session\nswitch. It receives no owner props; session facts arrive through the\nframework hooks of the `session-maybe` scope.",
|
|
2128
|
+
registerOptions: [],
|
|
2129
|
+
ownerProps: ["/** Conversation owner share: business state and actions belong to the registrant. */\nexport interface ConvOwnerProps {}"],
|
|
2130
|
+
ownerPropsReferences: [],
|
|
2131
|
+
standardProps: [
|
|
2132
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2133
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2134
|
+
"useSession: MaybeSnapshotSelectorHook<ConversationSnapshot>",
|
|
2135
|
+
"sessionId: SessionId | undefined",
|
|
2136
|
+
"useProjection: UseProjection",
|
|
2137
|
+
"useInput: MaybeSnapshotSelectorHook<InputState>",
|
|
2138
|
+
"inputActions: InputActions | undefined"
|
|
2139
|
+
],
|
|
2140
|
+
keyDomain: "",
|
|
2141
|
+
hookContext: "",
|
|
2142
|
+
slotInject: "",
|
|
2143
|
+
declaredBy: "an entry in 'root' (client-ui-layout), so it exists while that entry is mounted",
|
|
2144
|
+
occupants: ["client-ui-conversation ConversationRoot"],
|
|
2145
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2146
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation', () => ctx.slots.register(\n { name: 'conversation' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2147
|
+
source: "packages/client/ui-layout/src/client/index.ts:62"
|
|
2148
|
+
},
|
|
2149
|
+
{
|
|
2150
|
+
key: "conversation.chat.assistant-actions",
|
|
2151
|
+
kind: "list",
|
|
2152
|
+
scope: "session",
|
|
2153
|
+
summary: "Action strip attached to one finalized assistant message, rendered inside that message's IconActions row.",
|
|
2154
|
+
doc: "Action strip attached to one finalized assistant message, rendered\ninside that message's IconActions row. The chat entry owns the render\nsite and passes the addressed message identity; contributors add\nper-message actions without importing the conversation implementation.\nEntries render by ascending `order`.",
|
|
2155
|
+
registerOptions: [
|
|
2156
|
+
{
|
|
2157
|
+
name: "id",
|
|
2158
|
+
requirement: "required",
|
|
2159
|
+
type: "string",
|
|
2160
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
name: "order",
|
|
2164
|
+
requirement: "optional",
|
|
2165
|
+
type: "number",
|
|
2166
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
2167
|
+
},
|
|
2168
|
+
{
|
|
2169
|
+
name: "label",
|
|
2170
|
+
requirement: "optional",
|
|
2171
|
+
type: "string | (() => string)",
|
|
2172
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
2173
|
+
}
|
|
2174
|
+
],
|
|
2175
|
+
ownerProps: ["/**\n * Owner currency of the assistant-message action strip: the durable identity\n * of the one finalized message the contributed actions address. Only finalized\n * messages reach this slot, so the id is always present.\n */\nexport interface AssistantActionOwnerProps {\n /** Stable identity carried from the `assistant/message` event. */\n messageId: MessageId\n}"],
|
|
2176
|
+
ownerPropsReferences: ["MessageId"],
|
|
2177
|
+
standardProps: [
|
|
2178
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2179
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2180
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2181
|
+
"sessionId: SessionId",
|
|
2182
|
+
"useProjection: UseProjection",
|
|
2183
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2184
|
+
"inputActions: InputActions"
|
|
2185
|
+
],
|
|
2186
|
+
keyDomain: "",
|
|
2187
|
+
hookContext: "",
|
|
2188
|
+
slotInject: "",
|
|
2189
|
+
declaredBy: "an entry in 'conversation.chat.node' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2190
|
+
occupants: ["client-ui-message-feedback MessageFeedbackActions id 'feedback'"],
|
|
2191
|
+
replaceRisk: "none",
|
|
2192
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.chat.assistant-actions', () => ctx.slots.register(\n { name: 'conversation.chat.assistant-actions', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2193
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:148"
|
|
2194
|
+
},
|
|
2195
|
+
{
|
|
2196
|
+
key: "conversation.chat.commandview",
|
|
2197
|
+
kind: "keyed",
|
|
2198
|
+
scope: "session",
|
|
2199
|
+
summary: "The chat view's per-command row hole: keyed dispatch on the command name (`command/run.name`; a run-less cross-window node has none and always lands on the fallback).",
|
|
2200
|
+
doc: "The chat view's per-command row hole: keyed dispatch on the command\nname (`command/run.name`; a run-less cross-window node has none and\nalways lands on the fallback). Declared by the chat view entry; the\nrender site dispatches via `entryKey: name` with GenericCommandCard as\nthe `fallback` — a slash command renders durably with zero\nregistration, and a domain upgrades by registering one row component.",
|
|
2201
|
+
registerOptions: [{
|
|
2202
|
+
name: "key",
|
|
2203
|
+
requirement: "required",
|
|
2204
|
+
type: "string",
|
|
2205
|
+
doc: "Your cell key: the entry renders where the owner dispatches this exact key. Registering an already-occupied key replaces that occupant."
|
|
2206
|
+
}],
|
|
2207
|
+
ownerProps: ["/**\n * Owner share of the per-command row slot: the frozen {@link CommandNode}\n * slice off the snapshot (cache-stable reference — memo premise). The node\n * carries the whole lifecycle (structured name/args, pairing id, and\n * outcome-or-executing). A successful domain command may also carry the\n * explicitly linked projection node needed to fold two log records into one\n * presentation row.\n */\nexport interface CommandRowOwnerProps {\n /** Folded command lifecycle node (run + optional done). */\n node: CommandNode\n /** Explicitly linked compaction checkpoint for the settled `/compact` presentation. */\n compaction?: CompactionSummaryNode\n}"],
|
|
2208
|
+
ownerPropsReferences: ["CommandNode", "CompactionSummaryNode"],
|
|
2209
|
+
standardProps: [
|
|
2210
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2211
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2212
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2213
|
+
"sessionId: SessionId",
|
|
2214
|
+
"useProjection: UseProjection",
|
|
2215
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2216
|
+
"inputActions: InputActions"
|
|
2217
|
+
],
|
|
2218
|
+
keyDomain: "open: any string the owner dispatches (no compile-time key set), none are taken yet",
|
|
2219
|
+
hookContext: "",
|
|
2220
|
+
slotInject: "",
|
|
2221
|
+
declaredBy: "an entry in 'conversation.chat.node' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2222
|
+
occupants: [],
|
|
2223
|
+
replaceRisk: "none",
|
|
2224
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.chat.commandview', () => ctx.slots.register(\n { name: 'conversation.chat.commandview', key: '<one key the owner dispatches>' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2225
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:133"
|
|
2226
|
+
},
|
|
2227
|
+
{
|
|
2228
|
+
key: "conversation.chat.node",
|
|
2229
|
+
kind: "keyed",
|
|
2230
|
+
scope: "session",
|
|
2231
|
+
summary: "Final business node renderer, dispatched by `ChatConversationViewNode.kind`.",
|
|
2232
|
+
doc: "Final business node renderer, dispatched by `ChatConversationViewNode.kind`.",
|
|
2233
|
+
registerOptions: [{
|
|
2234
|
+
name: "key",
|
|
2235
|
+
requirement: "required",
|
|
2236
|
+
type: "string",
|
|
2237
|
+
doc: "Your cell key: the entry renders where the owner dispatches this exact key. Registering an already-occupied key replaces that occupant."
|
|
2238
|
+
}],
|
|
2239
|
+
ownerProps: ["/** Stable owner currency delivered to one keyed Chat business renderer. */\nexport interface ChatNodeOwnerProps {\n /** Selected Tool call, when the shared details store names one. */\n selectedCallId?: CallId | undefined\n /** Session workspace root; Tool summaries display paths relative to it. */\n cwd?: string | undefined\n openFile: (path: string) => void\n inspectCall: (callId: CallId) => void\n forkAt: (seq: number) => void\n /** Render a historical image group through the attachment slot. */\n renderMessageImages: RenderMessageImages\n fileMentions: (owner: TurnTailOwnerProps) => MarkdownFileMentions | undefined\n}"],
|
|
2240
|
+
ownerPropsReferences: [
|
|
2241
|
+
"MarkdownFileMentions",
|
|
2242
|
+
"RenderMessageImages",
|
|
2243
|
+
"TurnTailOwnerProps"
|
|
2244
|
+
],
|
|
2245
|
+
standardProps: [
|
|
2246
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2247
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2248
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2249
|
+
"sessionId: SessionId",
|
|
2250
|
+
"useProjection: UseProjection",
|
|
2251
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2252
|
+
"inputActions: InputActions"
|
|
2253
|
+
],
|
|
2254
|
+
keyDomain: "fixed by the owner's key table { [Kind in ChatNodeKind]: { node: ChatNode<Kind> } }, already taken: assistant-step, command, command-input, compaction, context, manual-compaction, model-retry, steering, tool-call, turn-error, turn-max-tokens, turn-tail, unknown, user, workflow-run",
|
|
2255
|
+
hookContext: "string",
|
|
2256
|
+
slotInject: "ChatNodeTurnDataInjected",
|
|
2257
|
+
declaredBy: "an entry in 'conversation.view' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2258
|
+
occupants: [
|
|
2259
|
+
"client-ui-conversation UserMessageNodeView key 'user'",
|
|
2260
|
+
"client-ui-conversation UserMessageNodeView key 'steering'",
|
|
2261
|
+
"client-ui-conversation ContextMessageNodeView key 'context'",
|
|
2262
|
+
"client-ui-conversation AssistantNodeView key 'assistant-step'",
|
|
2263
|
+
"client-ui-conversation CommandNodeView key 'command'",
|
|
2264
|
+
"client-ui-conversation ManualCompactionNodeView key 'manual-compaction'",
|
|
2265
|
+
"client-ui-conversation CompactionNodeView key 'compaction'",
|
|
2266
|
+
"client-ui-conversation RetryNodeView key 'model-retry'",
|
|
2267
|
+
"client-ui-conversation TurnErrorNodeView key 'turn-error'",
|
|
2268
|
+
"client-ui-conversation TurnMaxTokensNodeView key 'turn-max-tokens'",
|
|
2269
|
+
"client-ui-conversation TurnTailNodeView key 'turn-tail'",
|
|
2270
|
+
"client-ui-conversation UnknownNodeView key 'unknown'",
|
|
2271
|
+
"client-ui-goal GoalCommandInputView key 'command-input'",
|
|
2272
|
+
"client-ui-tool ToolCallTree key 'tool-call'",
|
|
2273
|
+
"client-ui-workflow-run WorkflowRunPanel key 'workflow-run'"
|
|
2274
|
+
],
|
|
2275
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2276
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.chat.node', () => ctx.slots.register(\n { name: 'conversation.chat.node', key: '<one key the owner dispatches>' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2277
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:115"
|
|
2278
|
+
},
|
|
2279
|
+
{
|
|
2280
|
+
key: "conversation.chat.turnTail",
|
|
2281
|
+
kind: "chain",
|
|
2282
|
+
scope: "session",
|
|
2283
|
+
summary: "The completed Turn Node's extension chain, rendered before that Node's IconActions.",
|
|
2284
|
+
doc: "The completed Turn Node's extension chain, rendered before that Node's\nIconActions. Entries derive a match from the engine-owned Turn and\nclosing seq before mounting, so presentation components never mount\nonly to return null; an all-declined chain renders nothing.",
|
|
2285
|
+
registerOptions: [{
|
|
2286
|
+
name: "select",
|
|
2287
|
+
requirement: "required",
|
|
2288
|
+
type: "(owner) => unknown | null",
|
|
2289
|
+
doc: "Pure routing selector. Entries are tried in ascending order; the first non-null result wins and arrives as the component's `matched` prop. All-null falls through to the owner's fallback."
|
|
2290
|
+
}],
|
|
2291
|
+
ownerProps: ["/**\n * Owner currency of the chat view's turn-tail hole: the engine-owned Turn and\n * the closing assistant's anchor. Registrants read their own typed Turn data\n * and open files through the same opener the tool rows use.\n */\nexport interface TurnTailOwnerProps {\n /** Engine-owned closing Turn boundary. */\n turn: TurnLocation\n /** The closing assistant's seq — the anchor the tail renders under. */\n seq: number\n /**\n * Open a filesystem path through the Host (tool-row semantics; the chat\n * view resolves relative paths against the session cwd).\n */\n openFile: (path: string) => void\n}"],
|
|
2292
|
+
ownerPropsReferences: ["TurnLocation"],
|
|
2293
|
+
standardProps: [
|
|
2294
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2295
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2296
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2297
|
+
"sessionId: SessionId",
|
|
2298
|
+
"useProjection: UseProjection",
|
|
2299
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2300
|
+
"inputActions: InputActions"
|
|
2301
|
+
],
|
|
2302
|
+
keyDomain: "",
|
|
2303
|
+
hookContext: "",
|
|
2304
|
+
slotInject: "",
|
|
2305
|
+
declaredBy: "an entry in 'conversation.chat.node' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2306
|
+
occupants: ["client-ui-deliverables ProducedFiles"],
|
|
2307
|
+
replaceRisk: "none",
|
|
2308
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.chat.turnTail', () => ctx.slots.register(\n { name: 'conversation.chat.turnTail', select: owner => null },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2309
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:140"
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
key: "conversation.composer",
|
|
2313
|
+
kind: "chain",
|
|
2314
|
+
scope: "session",
|
|
2315
|
+
summary: "The composer takeover chain: entries are selector-routed replacements of the default InputBar.",
|
|
2316
|
+
doc: "The composer takeover chain: entries are selector-routed replacements\nof the default InputBar. Declared by this package's 'conversation'\nentry; the owner dispatches the ComposerChainProps currency and\nrouting lives in entry selectors — new takeover kinds register with\nzero owner changes.",
|
|
2317
|
+
registerOptions: [{
|
|
2318
|
+
name: "select",
|
|
2319
|
+
requirement: "required",
|
|
2320
|
+
type: "(owner) => unknown | null",
|
|
2321
|
+
doc: "Pure routing selector. Entries are tried in ascending order; the first non-null result wins and arrives as the component's `matched` prop. All-null falls through to the owner's fallback."
|
|
2322
|
+
}],
|
|
2323
|
+
ownerProps: ["/**\n * Composer chain currency: what ConversationRoot dispatches at its\n * renderSlotChain site. The owner declares the currency only — never a\n * per-entry contract; takeover packages narrow it in their own selectors\n * (`interactions.find(i => i.kind === ...)`), so new takeover kinds register\n * with zero owner changes.\n */\nexport interface ComposerChainProps {\n interactions: readonly PendingInteraction[]\n /** Current conversation facts for feature-owned takeover selectors. */\n session: ConversationSnapshot | undefined\n}"],
|
|
2324
|
+
ownerPropsReferences: ["ConversationSnapshot", "PendingInteraction"],
|
|
2325
|
+
standardProps: [
|
|
2326
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2327
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2328
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2329
|
+
"sessionId: SessionId",
|
|
2330
|
+
"useProjection: UseProjection",
|
|
2331
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2332
|
+
"inputActions: InputActions"
|
|
2333
|
+
],
|
|
2334
|
+
keyDomain: "",
|
|
2335
|
+
hookContext: "",
|
|
2336
|
+
slotInject: "",
|
|
2337
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2338
|
+
occupants: [
|
|
2339
|
+
"client-ui-conversation ApprovalPanel",
|
|
2340
|
+
"client-ui-subagent SubagentReadOnlyComposer",
|
|
2341
|
+
"client-ui-user-questions QuestionComposer"
|
|
2342
|
+
],
|
|
2343
|
+
replaceRisk: "none",
|
|
2344
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.composer', () => ctx.slots.register(\n { name: 'conversation.composer', select: owner => null },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2345
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:171"
|
|
2346
|
+
},
|
|
2347
|
+
{
|
|
2348
|
+
key: "conversation.composer.bar",
|
|
2349
|
+
kind: "single",
|
|
2350
|
+
scope: "session-maybe",
|
|
2351
|
+
summary: "The default composer body: a single slot rendered as the composer chain's fallback (a real entry, not a chain rider, so a takeover election hides rather than unmounts it and the textarea DOM survives).",
|
|
2352
|
+
doc: "The default composer body: a single slot rendered as the composer\nchain's fallback (a real entry, not a chain rider, so a\ntakeover election hides rather than unmounts it and the textarea DOM\nsurvives). Session-maybe: the bar stays mounted across the\nno-session/session transition — the no-workspace hero renders the SAME\ntextarea DOM as a read-only Workspace-picker trigger instead of a\nparallel inert tree — with the machine hooks absent until a session is\ncurrent. InputBar registers\nhere from this package's apply; its machine state arrives through the\nstandard provide channel (useInput + inputActions), the keyboard\ncommand face through its own inject.",
|
|
2353
|
+
registerOptions: [],
|
|
2354
|
+
ownerProps: ["/**\n * Owner share of the composer-bar slot: ConversationRoot's layout-phase\n * inputs plus the input-region child-slot content it renders (the region\n * slots stay declared/rendered by the conversation entry; the bar hosts the\n * results as chrome).\n */\nexport interface ComposerBarOwnerProps {\n /** Hero = empty-state centered card; composer = resident bottom bar. */\n variant: 'hero' | 'composer'\n /**\n * A block another plugin raised for this session: the bar refuses input and\n * shows the blocker's reason as the placeholder, but — unlike `disabled` —\n * keeps the model seat live. Every block this contract has is one the user\n * clears by choosing a model, so locking that seat too would leave the\n * composer telling them to do the one thing it prevents.\n */\n blocked?: { readonly reason: string }\n /**\n * Inert no-workspace state: the bar locks message actions while preserving\n * its normal DOM so the Workspace pick transitions in place.\n */\n disabled?: boolean\n /** Whether the shared Workspace picker menu is expanded, regardless of which trigger opened it. */\n workspacePickerOpen?: boolean\n /** Open the existing Workspace picker from the inert textarea. */ /* …truncated — full shape in source */"],
|
|
2355
|
+
ownerPropsReferences: ["Workspace"],
|
|
2356
|
+
standardProps: [
|
|
2357
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2358
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2359
|
+
"useSession: MaybeSnapshotSelectorHook<ConversationSnapshot>",
|
|
2360
|
+
"sessionId: SessionId | undefined",
|
|
2361
|
+
"useProjection: UseProjection",
|
|
2362
|
+
"useInput: MaybeSnapshotSelectorHook<InputState>",
|
|
2363
|
+
"inputActions: InputActions | undefined"
|
|
2364
|
+
],
|
|
2365
|
+
keyDomain: "",
|
|
2366
|
+
hookContext: "",
|
|
2367
|
+
slotInject: "",
|
|
2368
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2369
|
+
occupants: ["client-ui-conversation InputBar"],
|
|
2370
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2371
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.composer.bar', () => ctx.slots.register(\n { name: 'conversation.composer.bar' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2372
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:245"
|
|
2373
|
+
},
|
|
2374
|
+
{
|
|
2375
|
+
key: "conversation.composer.dock",
|
|
2376
|
+
kind: "list",
|
|
2377
|
+
scope: "session",
|
|
2378
|
+
summary: "The band under the composer card, inside the bar's width column — the seat for an ambient readout about the conversation (the shipped stats line lives here).",
|
|
2379
|
+
doc: "The band under the composer card, inside the bar's width column — the\nseat for an ambient readout about the conversation (the shipped stats\nline lives here). Same InputZone owner share as the other\nregions. Anything the user must click belongs in the tool row instead\n(`conversation.input.left` / `.right`); anything needing its own line\nabove the card belongs in `conversation.input.dock`.",
|
|
2380
|
+
registerOptions: [
|
|
2381
|
+
{
|
|
2382
|
+
name: "id",
|
|
2383
|
+
requirement: "required",
|
|
2384
|
+
type: "string",
|
|
2385
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
2386
|
+
},
|
|
2387
|
+
{
|
|
2388
|
+
name: "order",
|
|
2389
|
+
requirement: "optional",
|
|
2390
|
+
type: "number",
|
|
2391
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
2392
|
+
},
|
|
2393
|
+
{
|
|
2394
|
+
name: "label",
|
|
2395
|
+
requirement: "optional",
|
|
2396
|
+
type: "string | (() => string)",
|
|
2397
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
2398
|
+
}
|
|
2399
|
+
],
|
|
2400
|
+
ownerProps: ["/**\n * The input-region slot currency: dock/left/right entries read\n * the conversation snapshot and the live input state as owner props (both\n * are point-in-time snapshots — the dispatching skeleton re-renders on\n * either store's change, so entries stay current without subscribing).\n */\nexport interface InputZone {\n readonly session: ConversationSnapshot\n readonly input: InputState\n}"],
|
|
2401
|
+
ownerPropsReferences: ["ConversationSnapshot", "InputState"],
|
|
2402
|
+
standardProps: [
|
|
2403
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2404
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2405
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2406
|
+
"sessionId: SessionId",
|
|
2407
|
+
"useProjection: UseProjection",
|
|
2408
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2409
|
+
"inputActions: InputActions"
|
|
2410
|
+
],
|
|
2411
|
+
keyDomain: "",
|
|
2412
|
+
hookContext: "",
|
|
2413
|
+
slotInject: "",
|
|
2414
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2415
|
+
occupants: ["client-ui-conversation StatsLine id 'stats'"],
|
|
2416
|
+
replaceRisk: "none",
|
|
2417
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register(\n { name: 'conversation.composer.dock', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2418
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:214"
|
|
2419
|
+
},
|
|
2420
|
+
{
|
|
2421
|
+
key: "conversation.details.tool",
|
|
2422
|
+
kind: "single",
|
|
2423
|
+
scope: "session",
|
|
2424
|
+
summary: "The body of the details panel for the tool call the user selected — one occupant, so taking it means rendering every tool's output, not just the ones you know.",
|
|
2425
|
+
doc: "The body of the details panel for the tool call the user selected —\none occupant, so taking it means rendering every tool's output, not just\nthe ones you know. The owner passes a frozen `block` whose two lifecycle\nforms must both be handled: branch on `'kind' in block` (a settled\n`ToolResultNode` has it, a still-running call does not), and treat\n`cwd` as display-only, for shortening workspace-rooted paths.\nA per-tool renderer belongs in the keyed `tool.call.toolview` seat\ninstead; this one is the whole panel.",
|
|
2426
|
+
registerOptions: [],
|
|
2427
|
+
ownerProps: ["/** Owner currency of the details panel's Tool output renderer. */\nexport interface DetailsToolOwnerProps {\n /** Frozen selected call slice. */\n block: ToolCallBlock\n /** Session workspace root for card cwd and relative-path display. */\n cwd?: string | undefined\n}"],
|
|
2428
|
+
ownerPropsReferences: [],
|
|
2429
|
+
standardProps: [
|
|
2430
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2431
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2432
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2433
|
+
"sessionId: SessionId",
|
|
2434
|
+
"useProjection: UseProjection",
|
|
2435
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2436
|
+
"inputActions: InputActions"
|
|
2437
|
+
],
|
|
2438
|
+
keyDomain: "",
|
|
2439
|
+
hookContext: "",
|
|
2440
|
+
slotInject: "",
|
|
2441
|
+
declaredBy: "an entry in 'details' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2442
|
+
occupants: ["client-ui-tool ToolDetails"],
|
|
2443
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2444
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.details.tool', () => ctx.slots.register(\n { name: 'conversation.details.tool' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2445
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:163"
|
|
2446
|
+
},
|
|
2447
|
+
{
|
|
2448
|
+
key: "conversation.hero.agentPreset",
|
|
2449
|
+
kind: "single",
|
|
2450
|
+
scope: "root",
|
|
2451
|
+
summary: "The agent-preset chip beside the workspace picker on the new-session screen.",
|
|
2452
|
+
doc: "The agent-preset chip beside the workspace picker on the new-session\nscreen. Root scope: no session exists yet, so the choice is staged for\nthe next one rather than applied to a current one.",
|
|
2453
|
+
registerOptions: [],
|
|
2454
|
+
ownerProps: ["/** Owner share of the hero agent-preset chip: the shell supplies nothing. */\nexport interface HeroAgentPresetOwnerProps {\n /** Marker field: the chip owns its own roster, staging, and menu state. */\n children?: never\n}"],
|
|
2455
|
+
ownerPropsReferences: [],
|
|
2456
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
2457
|
+
keyDomain: "",
|
|
2458
|
+
hookContext: "",
|
|
2459
|
+
slotInject: "",
|
|
2460
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2461
|
+
occupants: ["client-ui-agent-preset AgentPresetSeat"],
|
|
2462
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2463
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.hero.agentPreset', () => ctx.slots.register(\n { name: 'conversation.hero.agentPreset' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2464
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:189"
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
key: "conversation.hero.brand.mark",
|
|
2468
|
+
kind: "single",
|
|
2469
|
+
scope: "root",
|
|
2470
|
+
summary: "Brand mark leading the blank-session headline.",
|
|
2471
|
+
doc: "Brand mark leading the blank-session headline. Declared by this\npackage's `conversation` entry; the shell supplies a fish fallback.",
|
|
2472
|
+
registerOptions: [],
|
|
2473
|
+
ownerProps: ["/** Presentation props supplied to the blank-session brand-mark occupant. */\nexport interface HeroBrandMarkOwnerProps {\n /** Requested square edge in pixels. */\n size: number\n /** Host CSS class for preserving the default hero mark color and hover motion. */\n className?: string | undefined\n}"],
|
|
2474
|
+
ownerPropsReferences: [],
|
|
2475
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
2476
|
+
keyDomain: "",
|
|
2477
|
+
hookContext: "",
|
|
2478
|
+
slotInject: "",
|
|
2479
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2480
|
+
occupants: ["client-ui-brand-official OfficialBrandMark"],
|
|
2481
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2482
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.hero.brand.mark', () => ctx.slots.register(\n { name: 'conversation.hero.brand.mark' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2483
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:183"
|
|
2484
|
+
},
|
|
2485
|
+
{
|
|
2486
|
+
key: "conversation.hero.workspace",
|
|
2487
|
+
kind: "single",
|
|
2488
|
+
scope: "root",
|
|
2489
|
+
summary: "The hero-phase Workspace picker hole: rendered by ConversationRoot while the session is blank (picking another workspace switches to that workspace's blank session, draft carried).",
|
|
2490
|
+
doc: "The hero-phase Workspace picker hole: rendered by ConversationRoot\nwhile the session is blank (picking another workspace switches to that\nworkspace's blank session, draft carried). Root scope: the picker\nreads the global workspace list.",
|
|
2491
|
+
registerOptions: [],
|
|
2492
|
+
ownerProps: ["/** Owner share common to the hero / New-Session Workspace pickers. */\nexport interface EmptyWorkspaceOwnerProps {\n open: boolean\n anchorRef?: RefObject<HTMLElement>\n /** Currently active workspace (renders a trailing check in the picker list). */\n selectedId?: WorkspaceId | undefined\n onPick: (workspaceId: WorkspaceId) => void\n onClose: () => void\n}"],
|
|
2493
|
+
ownerPropsReferences: ["Workspace"],
|
|
2494
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
2495
|
+
keyDomain: "",
|
|
2496
|
+
hookContext: "",
|
|
2497
|
+
slotInject: "",
|
|
2498
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2499
|
+
occupants: ["client-ui-workspace WorkspacePicker"],
|
|
2500
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2501
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.hero.workspace', () => ctx.slots.register(\n { name: 'conversation.hero.workspace' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2502
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:178"
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
key: "conversation.hero.workspace.directoryFlow",
|
|
2506
|
+
kind: "single",
|
|
2507
|
+
scope: "root",
|
|
2508
|
+
summary: "Directory-flow hole under the conversation empty-state picker (declared by the WorkspacePicker entry).",
|
|
2509
|
+
doc: "Directory-flow hole under the conversation empty-state picker (declared by the WorkspacePicker entry).",
|
|
2510
|
+
registerOptions: [],
|
|
2511
|
+
ownerProps: ["/**\n * Owner share of the directory-flow holes: the complete conversation between\n * the trigger surface and the picking interaction. The occupant reads `open`\n * to run/render its interaction and reports exactly one outcome per open.\n */\nexport interface DirectoryFlowOwnerProps {\n /** True while a picking interaction is requested; flipping back to false withdraws the request. */\n open: boolean\n /** True while the owner adopts a picked path (`createWorkspace` in flight); occupants disable their commit affordances. */\n busy: boolean\n /** The operator picked a directory (absolute host path); the owner adopts it. */\n onPicked: (path: string) => void\n /** The operator dismissed the interaction; the owner just closes the flow. */\n onCancel: () => void\n /** The interaction itself failed (chooser missing, listing denied); the owner shows its error surface. */\n onError: (message: string) => void\n}"],
|
|
2512
|
+
ownerPropsReferences: [],
|
|
2513
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
2514
|
+
keyDomain: "",
|
|
2515
|
+
hookContext: "",
|
|
2516
|
+
slotInject: "",
|
|
2517
|
+
declaredBy: "an entry in 'conversation.hero.workspace' (client-ui-workspace), so it exists while that entry is mounted",
|
|
2518
|
+
occupants: ["client-ui-directory-picker-browse BrowseDirectoryFlow", "client-ui-directory-picker-native NativeDirectoryFlow"],
|
|
2519
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2520
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.hero.workspace.directoryFlow', () => ctx.slots.register(\n { name: 'conversation.hero.workspace.directoryFlow' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2521
|
+
source: "packages/client/ui-workspace/src/client/contract/slots.ts:57"
|
|
2522
|
+
},
|
|
2523
|
+
{
|
|
2524
|
+
key: "conversation.input.attachments",
|
|
2525
|
+
kind: "single",
|
|
2526
|
+
scope: "session-maybe",
|
|
2527
|
+
summary: "Optional draft-image rail, drop target, and preview surface inside the composer.",
|
|
2528
|
+
doc: "Optional draft-image rail, drop target, and preview surface inside the composer.",
|
|
2529
|
+
registerOptions: [],
|
|
2530
|
+
ownerProps: ["/** Input state handed to the optional attachment presentation plugin. */\nexport interface ComposerAttachmentsOwnerProps {\n /** Browser-owned draft images in input order. */\n attachments: readonly ComposerAttachment[]\n /** Whether a document-level file drop may add images now. */\n canAcceptDrop: boolean\n /** Add one dropped batch through the composer's validation path. */\n onAddImages: (files: readonly File[]) => void\n /** Remove one draft image through the conversation service. */\n onRemoveImage: (id: DraftAttachmentId) => void\n /** Display-ready limits for the drop invitation. */\n dropLimits?: { readonly count: number; readonly size: string } | undefined\n}"],
|
|
2531
|
+
ownerPropsReferences: ["ComposerAttachment", "DraftAttachmentId"],
|
|
2532
|
+
standardProps: [
|
|
2533
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2534
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2535
|
+
"useSession: MaybeSnapshotSelectorHook<ConversationSnapshot>",
|
|
2536
|
+
"sessionId: SessionId | undefined",
|
|
2537
|
+
"useProjection: UseProjection",
|
|
2538
|
+
"useInput: MaybeSnapshotSelectorHook<InputState>",
|
|
2539
|
+
"inputActions: InputActions | undefined"
|
|
2540
|
+
],
|
|
2541
|
+
keyDomain: "",
|
|
2542
|
+
hookContext: "",
|
|
2543
|
+
slotInject: "",
|
|
2544
|
+
declaredBy: "an entry in 'conversation.composer.bar' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2545
|
+
occupants: ["client-ui-attachment ComposerAttachments"],
|
|
2546
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2547
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.input.attachments', () => ctx.slots.register(\n { name: 'conversation.input.attachments' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2548
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:247"
|
|
2549
|
+
},
|
|
2550
|
+
{
|
|
2551
|
+
key: "conversation.input.dock",
|
|
2552
|
+
kind: "list",
|
|
2553
|
+
scope: "session",
|
|
2554
|
+
summary: "A full-width row of its own, stacked above the composer card — the seat for anything that needs a line to itself (queue rows, a todo strip, a goal bar).",
|
|
2555
|
+
doc: "A full-width row of its own, stacked above the composer card — the seat\nfor anything that needs a line to itself (queue rows, a todo strip, a\ngoal bar). Pick this over the three seats below when your content wraps\nor carries prose; pick `conversation.composer.dock` for an ambient\nreadout under the card, and `conversation.input.left` /\n`.right` for a small control INSIDE the card's tool row.\nRead only `session`/`input` off the owner share (InputZone) —\nboth are point-in-time snapshots re-rendered for you, never subscribe.",
|
|
2556
|
+
registerOptions: [
|
|
2557
|
+
{
|
|
2558
|
+
name: "id",
|
|
2559
|
+
requirement: "required",
|
|
2560
|
+
type: "string",
|
|
2561
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
2562
|
+
},
|
|
2563
|
+
{
|
|
2564
|
+
name: "order",
|
|
2565
|
+
requirement: "optional",
|
|
2566
|
+
type: "number",
|
|
2567
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
2568
|
+
},
|
|
2569
|
+
{
|
|
2570
|
+
name: "label",
|
|
2571
|
+
requirement: "optional",
|
|
2572
|
+
type: "string | (() => string)",
|
|
2573
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
2574
|
+
}
|
|
2575
|
+
],
|
|
2576
|
+
ownerProps: ["/**\n * The input-region slot currency: dock/left/right entries read\n * the conversation snapshot and the live input state as owner props (both\n * are point-in-time snapshots — the dispatching skeleton re-renders on\n * either store's change, so entries stay current without subscribing).\n */\nexport interface InputZone {\n readonly session: ConversationSnapshot\n readonly input: InputState\n}"],
|
|
2577
|
+
ownerPropsReferences: ["ConversationSnapshot", "InputState"],
|
|
2578
|
+
standardProps: [
|
|
2579
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2580
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2581
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2582
|
+
"sessionId: SessionId",
|
|
2583
|
+
"useProjection: UseProjection",
|
|
2584
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2585
|
+
"inputActions: InputActions"
|
|
2586
|
+
],
|
|
2587
|
+
keyDomain: "",
|
|
2588
|
+
hookContext: "",
|
|
2589
|
+
slotInject: "",
|
|
2590
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2591
|
+
occupants: [
|
|
2592
|
+
"client-ui-conversation QueueDock id 'queue'",
|
|
2593
|
+
"client-ui-conversation TodoDock id 'todo'",
|
|
2594
|
+
"client-ui-goal GoalDock id 'goal'"
|
|
2595
|
+
],
|
|
2596
|
+
replaceRisk: "none",
|
|
2597
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.input.dock', () => ctx.slots.register(\n { name: 'conversation.input.dock', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2598
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:205"
|
|
2599
|
+
},
|
|
2600
|
+
{
|
|
2601
|
+
key: "conversation.input.left",
|
|
2602
|
+
kind: "list",
|
|
2603
|
+
scope: "session",
|
|
2604
|
+
summary: "The left end of the tool row INSIDE the composer card, after the resident chrome (access mode, plan, attach) — the seat for a small always-visible control.",
|
|
2605
|
+
doc: "The left end of the tool row INSIDE the composer card, after the\nresident chrome (access mode, plan, attach) — the seat for a small\nalways-visible control. Entries sit beside that chrome, never replace\nit. Same InputZone owner share; use `.right` for a control that\nbelongs next to the send button, and the docks for anything taller than\none row.",
|
|
2606
|
+
registerOptions: [
|
|
2607
|
+
{
|
|
2608
|
+
name: "id",
|
|
2609
|
+
requirement: "required",
|
|
2610
|
+
type: "string",
|
|
2611
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
2612
|
+
},
|
|
2613
|
+
{
|
|
2614
|
+
name: "order",
|
|
2615
|
+
requirement: "optional",
|
|
2616
|
+
type: "number",
|
|
2617
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
2618
|
+
},
|
|
2619
|
+
{
|
|
2620
|
+
name: "label",
|
|
2621
|
+
requirement: "optional",
|
|
2622
|
+
type: "string | (() => string)",
|
|
2623
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
2624
|
+
}
|
|
2625
|
+
],
|
|
2626
|
+
ownerProps: ["/**\n * The input-region slot currency: dock/left/right entries read\n * the conversation snapshot and the live input state as owner props (both\n * are point-in-time snapshots — the dispatching skeleton re-renders on\n * either store's change, so entries stay current without subscribing).\n */\nexport interface InputZone {\n readonly session: ConversationSnapshot\n readonly input: InputState\n}"],
|
|
2627
|
+
ownerPropsReferences: ["ConversationSnapshot", "InputState"],
|
|
2628
|
+
standardProps: [
|
|
2629
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2630
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2631
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2632
|
+
"sessionId: SessionId",
|
|
2633
|
+
"useProjection: UseProjection",
|
|
2634
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2635
|
+
"inputActions: InputActions"
|
|
2636
|
+
],
|
|
2637
|
+
keyDomain: "",
|
|
2638
|
+
hookContext: "",
|
|
2639
|
+
slotInject: "",
|
|
2640
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2641
|
+
occupants: [],
|
|
2642
|
+
replaceRisk: "none",
|
|
2643
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.input.left', () => ctx.slots.register(\n { name: 'conversation.input.left', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2644
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:223"
|
|
2645
|
+
},
|
|
2646
|
+
{
|
|
2647
|
+
key: "conversation.input.model",
|
|
2648
|
+
kind: "single",
|
|
2649
|
+
scope: "session",
|
|
2650
|
+
summary: "The named model-select seat at the right end of the composer tool row, left of the send button — one occupant, so taking it means rendering the whole model affordance yourself.",
|
|
2651
|
+
doc: "The named model-select seat at the right end of the composer tool row,\nleft of the send button — one occupant, so taking it means rendering the\nwhole model affordance yourself. Same `locked`-only owner share and same\nrenders-nothing-while-empty contract as the plan seat. Note the composer\ndeliberately keeps this seat LIVE while it refuses text for a\nmodel-related block: every such block is one the user clears by picking\na model here.",
|
|
2652
|
+
registerOptions: [],
|
|
2653
|
+
ownerProps: ["/**\n * Owner share of the two named composer control seats (plan / model): the\n * bar passes its disable state; the filling entry owns everything else.\n */\nexport interface InputControlOwnerProps {\n /** Session-removed lock (the bar's chrome disable state). */\n locked: boolean\n}"],
|
|
2654
|
+
ownerPropsReferences: [],
|
|
2655
|
+
standardProps: [
|
|
2656
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2657
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2658
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2659
|
+
"sessionId: SessionId",
|
|
2660
|
+
"useProjection: UseProjection",
|
|
2661
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2662
|
+
"inputActions: InputActions"
|
|
2663
|
+
],
|
|
2664
|
+
keyDomain: "",
|
|
2665
|
+
hookContext: "",
|
|
2666
|
+
slotInject: "",
|
|
2667
|
+
declaredBy: "an entry in 'conversation.composer.bar' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2668
|
+
occupants: ["client-ui-model-selection ModelSelect"],
|
|
2669
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2670
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.input.model', () => ctx.slots.register(\n { name: 'conversation.input.model' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2671
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:271"
|
|
2672
|
+
},
|
|
2673
|
+
{
|
|
2674
|
+
key: "conversation.input.overlay",
|
|
2675
|
+
kind: "list",
|
|
2676
|
+
scope: "session",
|
|
2677
|
+
summary: "The InputBar floating overlay anchor: MenuView (this package) and the popupSelect shell (ui-commands) contribute list entries; each reads its own store and renders null while closed.",
|
|
2678
|
+
doc: "The InputBar floating overlay anchor: MenuView (this package) and the\npopupSelect shell (ui-commands) contribute list entries; each reads its\nown store and renders null while closed. Declared (children table) by\nui-conversation's composer entry; the anchor hides with the input\nunder a takeover.",
|
|
2679
|
+
registerOptions: [
|
|
2680
|
+
{
|
|
2681
|
+
name: "id",
|
|
2682
|
+
requirement: "required",
|
|
2683
|
+
type: "string",
|
|
2684
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
2685
|
+
},
|
|
2686
|
+
{
|
|
2687
|
+
name: "order",
|
|
2688
|
+
requirement: "optional",
|
|
2689
|
+
type: "number",
|
|
2690
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
2691
|
+
},
|
|
2692
|
+
{
|
|
2693
|
+
name: "label",
|
|
2694
|
+
requirement: "optional",
|
|
2695
|
+
type: "string | (() => string)",
|
|
2696
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
2697
|
+
}
|
|
2698
|
+
],
|
|
2699
|
+
ownerProps: [],
|
|
2700
|
+
ownerPropsReferences: [],
|
|
2701
|
+
standardProps: [
|
|
2702
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2703
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2704
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2705
|
+
"sessionId: SessionId",
|
|
2706
|
+
"useProjection: UseProjection",
|
|
2707
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2708
|
+
"inputActions: InputActions"
|
|
2709
|
+
],
|
|
2710
|
+
keyDomain: "",
|
|
2711
|
+
hookContext: "",
|
|
2712
|
+
slotInject: "",
|
|
2713
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2714
|
+
occupants: ["client-ui-commands PopupSelectView id 'command-popup'", "client-ui-input-trigger MenuView id 'slash-menu'"],
|
|
2715
|
+
replaceRisk: "none",
|
|
2716
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.input.overlay', () => ctx.slots.register(\n { name: 'conversation.input.overlay', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2717
|
+
source: "packages/client/ui-input-trigger/src/client/slots.ts:24"
|
|
2718
|
+
},
|
|
2719
|
+
{
|
|
2720
|
+
key: "conversation.input.plan",
|
|
2721
|
+
kind: "single",
|
|
2722
|
+
scope: "session",
|
|
2723
|
+
summary: "The named plan-status seat in the composer tool row, immediately right of the access-mode control — one occupant, so taking it means rendering the plan affordance yourself.",
|
|
2724
|
+
doc: "The named plan-status seat in the composer tool row, immediately right\nof the access-mode control — one occupant, so taking it means rendering\nthe plan affordance yourself. The owner passes only `locked` (see\nInputControlOwnerProps): honour it by refusing interaction, and\ntake everything else from the framework session kit or your own inject.\nUnoccupied, the seat renders nothing at all — the bar paints no\nplaceholder, so an absent plan plugin costs no layout.",
|
|
2725
|
+
registerOptions: [],
|
|
2726
|
+
ownerProps: ["/**\n * Owner share of the two named composer control seats (plan / model): the\n * bar passes its disable state; the filling entry owns everything else.\n */\nexport interface InputControlOwnerProps {\n /** Session-removed lock (the bar's chrome disable state). */\n locked: boolean\n}"],
|
|
2727
|
+
ownerPropsReferences: [],
|
|
2728
|
+
standardProps: [
|
|
2729
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2730
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2731
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2732
|
+
"sessionId: SessionId",
|
|
2733
|
+
"useProjection: UseProjection",
|
|
2734
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2735
|
+
"inputActions: InputActions"
|
|
2736
|
+
],
|
|
2737
|
+
keyDomain: "",
|
|
2738
|
+
hookContext: "",
|
|
2739
|
+
slotInject: "",
|
|
2740
|
+
declaredBy: "an entry in 'conversation.composer.bar' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2741
|
+
occupants: ["client-ui-plan PlanChip"],
|
|
2742
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2743
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.input.plan', () => ctx.slots.register(\n { name: 'conversation.input.plan' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2744
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:261"
|
|
2745
|
+
},
|
|
2746
|
+
{
|
|
2747
|
+
key: "conversation.input.right",
|
|
2748
|
+
kind: "list",
|
|
2749
|
+
scope: "session",
|
|
2750
|
+
summary: "The right end of the same tool row, before the primary send button — the seat for a control the user reaches on the way to sending (the model select sits in its own named seat just left of here).",
|
|
2751
|
+
doc: "The right end of the same tool row, before the primary send button —\nthe seat for a control the user reaches on the way to sending (the\nmodel select sits in its own named seat just left of here). Same\nInputZone owner share and the same one-row height budget as\n`conversation.input.left`.",
|
|
2752
|
+
registerOptions: [
|
|
2753
|
+
{
|
|
2754
|
+
name: "id",
|
|
2755
|
+
requirement: "required",
|
|
2756
|
+
type: "string",
|
|
2757
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
2758
|
+
},
|
|
2759
|
+
{
|
|
2760
|
+
name: "order",
|
|
2761
|
+
requirement: "optional",
|
|
2762
|
+
type: "number",
|
|
2763
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
2764
|
+
},
|
|
2765
|
+
{
|
|
2766
|
+
name: "label",
|
|
2767
|
+
requirement: "optional",
|
|
2768
|
+
type: "string | (() => string)",
|
|
2769
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
2770
|
+
}
|
|
2771
|
+
],
|
|
2772
|
+
ownerProps: ["/**\n * The input-region slot currency: dock/left/right entries read\n * the conversation snapshot and the live input state as owner props (both\n * are point-in-time snapshots — the dispatching skeleton re-renders on\n * either store's change, so entries stay current without subscribing).\n */\nexport interface InputZone {\n readonly session: ConversationSnapshot\n readonly input: InputState\n}"],
|
|
2773
|
+
ownerPropsReferences: ["ConversationSnapshot", "InputState"],
|
|
2774
|
+
standardProps: [
|
|
2775
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2776
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2777
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2778
|
+
"sessionId: SessionId",
|
|
2779
|
+
"useProjection: UseProjection",
|
|
2780
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2781
|
+
"inputActions: InputActions"
|
|
2782
|
+
],
|
|
2783
|
+
keyDomain: "",
|
|
2784
|
+
hookContext: "",
|
|
2785
|
+
slotInject: "",
|
|
2786
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2787
|
+
occupants: [],
|
|
2788
|
+
replaceRisk: "none",
|
|
2789
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.input.right', () => ctx.slots.register(\n { name: 'conversation.input.right', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2790
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:231"
|
|
2791
|
+
},
|
|
2792
|
+
{
|
|
2793
|
+
key: "conversation.message.images",
|
|
2794
|
+
kind: "single",
|
|
2795
|
+
scope: "session",
|
|
2796
|
+
summary: "Optional renderer for one consecutive group of durable message images.",
|
|
2797
|
+
doc: "Optional renderer for one consecutive group of durable message images.",
|
|
2798
|
+
registerOptions: [],
|
|
2799
|
+
ownerProps: ["/** Historical image group handed to the optional attachment presentation plugin. */\nexport interface MessageImagesOwnerProps {\n /** Consecutive image blocks rendered as one gallery. */\n images: readonly { readonly attachment: ImageAttachmentRef }[]\n /** Session-authorized durable image loader. */\n loadImage: (attachment: ImageAttachmentRef) => Promise<string>\n /** Message-side alignment. */\n align: 'start' | 'end'\n}"],
|
|
2800
|
+
ownerPropsReferences: ["ImageAttachmentRef", "Message"],
|
|
2801
|
+
standardProps: [
|
|
2802
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2803
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2804
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2805
|
+
"sessionId: SessionId",
|
|
2806
|
+
"useProjection: UseProjection",
|
|
2807
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2808
|
+
"inputActions: InputActions"
|
|
2809
|
+
],
|
|
2810
|
+
keyDomain: "",
|
|
2811
|
+
hookContext: "",
|
|
2812
|
+
slotInject: "",
|
|
2813
|
+
declaredBy: "an entry in 'conversation.view' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2814
|
+
occupants: ["client-ui-attachment MessageImages"],
|
|
2815
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2816
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.message.images', () => ctx.slots.register(\n { name: 'conversation.message.images' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2817
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:124"
|
|
2818
|
+
},
|
|
2819
|
+
{
|
|
2820
|
+
key: "conversation.session",
|
|
2821
|
+
kind: "single",
|
|
2822
|
+
scope: "session",
|
|
2823
|
+
summary: "The entire body of one session: taking this seat means rendering that session's conversation yourself.",
|
|
2824
|
+
doc: "The entire body of one session: taking this seat means rendering that\nsession's conversation yourself. The occupant also owns the per-session\ndraft mirror and the active view ring, so a replacement inherits both\nduties and an empty one leaves a blank session pane — nothing here\ndegrades gracefully. To ADD rather than replace, take a seat inside the\nflow instead: `conversation.view` for a whole tab, the input regions for\ncomposer chrome.",
|
|
2825
|
+
registerOptions: [],
|
|
2826
|
+
ownerProps: [],
|
|
2827
|
+
ownerPropsReferences: [],
|
|
2828
|
+
standardProps: [
|
|
2829
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2830
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2831
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2832
|
+
"sessionId: SessionId",
|
|
2833
|
+
"useProjection: UseProjection",
|
|
2834
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2835
|
+
"inputActions: InputActions"
|
|
2836
|
+
],
|
|
2837
|
+
keyDomain: "",
|
|
2838
|
+
hookContext: "",
|
|
2839
|
+
slotInject: "",
|
|
2840
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2841
|
+
occupants: ["client-ui-conversation ConversationSession"],
|
|
2842
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2843
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.session', () => ctx.slots.register(\n { name: 'conversation.session' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2844
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:71"
|
|
2845
|
+
},
|
|
2846
|
+
{
|
|
2847
|
+
key: "conversation.session.header",
|
|
2848
|
+
kind: "single",
|
|
2849
|
+
scope: "session",
|
|
2850
|
+
summary: "The strip above the session's scrollport: title, view tabs, and the action row.",
|
|
2851
|
+
doc: "The strip above the session's scrollport: title, view tabs, and the\naction row. Taking this seat means rendering all three yourself, and it\nalso collapses `conversation.session.header.actions` — that additive\nseat is declared by whoever occupies this one, so replacing the header\ntakes every action entry down with it.",
|
|
2852
|
+
registerOptions: [],
|
|
2853
|
+
ownerProps: [],
|
|
2854
|
+
ownerPropsReferences: [],
|
|
2855
|
+
standardProps: [
|
|
2856
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2857
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2858
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2859
|
+
"sessionId: SessionId",
|
|
2860
|
+
"useProjection: UseProjection",
|
|
2861
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2862
|
+
"inputActions: InputActions"
|
|
2863
|
+
],
|
|
2864
|
+
keyDomain: "",
|
|
2865
|
+
hookContext: "",
|
|
2866
|
+
slotInject: "",
|
|
2867
|
+
declaredBy: "an entry in 'conversation' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2868
|
+
occupants: ["client-ui-conversation ConversationSessionHeader"],
|
|
2869
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2870
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.session.header', () => ctx.slots.register(\n { name: 'conversation.session.header' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2871
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:79"
|
|
2872
|
+
},
|
|
2873
|
+
{
|
|
2874
|
+
key: "conversation.session.header.actions",
|
|
2875
|
+
kind: "list",
|
|
2876
|
+
scope: "session",
|
|
2877
|
+
summary: "One button in the session header's action row — the additive way to put a per-session control beside the title without replacing the header.",
|
|
2878
|
+
doc: "One button in the session header's action row — the additive way to put\na per-session control beside the title without replacing the header.\nEntries render by ascending `order`; negative values are reserved for\nstatic session context that precedes interactive actions. The owner\npasses nothing: everything a control needs comes from the framework\nsession kit (`sessionId`, `useSession`, `useInput`, `inputActions`) and\nfrom the registrant's own inject face, so an empty owner share means\nself-sufficient, not starved.",
|
|
2879
|
+
registerOptions: [
|
|
2880
|
+
{
|
|
2881
|
+
name: "id",
|
|
2882
|
+
requirement: "required",
|
|
2883
|
+
type: "string",
|
|
2884
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
2885
|
+
},
|
|
2886
|
+
{
|
|
2887
|
+
name: "order",
|
|
2888
|
+
requirement: "optional",
|
|
2889
|
+
type: "number",
|
|
2890
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
2891
|
+
},
|
|
2892
|
+
{
|
|
2893
|
+
name: "label",
|
|
2894
|
+
requirement: "optional",
|
|
2895
|
+
type: "string | (() => string)",
|
|
2896
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
2897
|
+
}
|
|
2898
|
+
],
|
|
2899
|
+
ownerProps: ["/** Header actions derive their state from the standard session/global kit. */\nexport interface ConversationHeaderActionOwnerProps {}"],
|
|
2900
|
+
ownerPropsReferences: [],
|
|
2901
|
+
standardProps: [
|
|
2902
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2903
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2904
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2905
|
+
"sessionId: SessionId",
|
|
2906
|
+
"useProjection: UseProjection",
|
|
2907
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2908
|
+
"inputActions: InputActions"
|
|
2909
|
+
],
|
|
2910
|
+
keyDomain: "",
|
|
2911
|
+
hookContext: "",
|
|
2912
|
+
slotInject: "",
|
|
2913
|
+
declaredBy: "an entry in 'conversation.session.header' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2914
|
+
occupants: ["client-ui-agent-preset AgentPresetLabel id 'agent-preset'", "client-ui-jobs JobListAction id 'job-list'"],
|
|
2915
|
+
replaceRisk: "none",
|
|
2916
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.session.header.actions', () => ctx.slots.register(\n { name: 'conversation.session.header.actions', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2917
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:100"
|
|
2918
|
+
},
|
|
2919
|
+
{
|
|
2920
|
+
key: "conversation.session.header.lineage",
|
|
2921
|
+
kind: "single",
|
|
2922
|
+
scope: "session",
|
|
2923
|
+
summary: "One breadcrumb title and its lineage controls.",
|
|
2924
|
+
doc: "One breadcrumb title and its lineage controls. The render site keeps\nthe ordinary title as fallback; an occupant receives plain title data\nand may replace a subagent title with one combined navigation control.",
|
|
2925
|
+
registerOptions: [],
|
|
2926
|
+
ownerProps: ["/** Plain breadcrumb data handed to the optional lineage renderer. */\nexport interface ConversationHeaderLineageOwnerProps {\n /** Session represented by this breadcrumb title. */\n lineageSessionId: SessionId\n /** Display title available to a renderer that combines the title with a control. */\n displayTitle: string\n /** Navigate to an ancestor title when its combined control is clicked. */\n openTitle?: () => void\n}"],
|
|
2927
|
+
ownerPropsReferences: ["SessionId"],
|
|
2928
|
+
standardProps: [
|
|
2929
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2930
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2931
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2932
|
+
"sessionId: SessionId",
|
|
2933
|
+
"useProjection: UseProjection",
|
|
2934
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2935
|
+
"inputActions: InputActions"
|
|
2936
|
+
],
|
|
2937
|
+
keyDomain: "",
|
|
2938
|
+
hookContext: "",
|
|
2939
|
+
slotInject: "",
|
|
2940
|
+
declaredBy: "an entry in 'conversation.session.header' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2941
|
+
occupants: ["client-ui-subagent SubagentHeaderLineage"],
|
|
2942
|
+
replaceRisk: "shadows-shipped-ui",
|
|
2943
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.session.header.lineage', () => ctx.slots.register(\n { name: 'conversation.session.header.lineage' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2944
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:85"
|
|
2945
|
+
},
|
|
2946
|
+
{
|
|
2947
|
+
key: "conversation.session.header.utilities",
|
|
2948
|
+
kind: "list",
|
|
2949
|
+
scope: "session",
|
|
2950
|
+
summary: "Right-aligned Session utilities kept outside the title-adjacent action group, so an optional utility cannot reorder session context or lineage.",
|
|
2951
|
+
doc: "Right-aligned Session utilities kept outside the title-adjacent action\ngroup, so an optional utility cannot reorder session context or lineage.",
|
|
2952
|
+
registerOptions: [
|
|
2953
|
+
{
|
|
2954
|
+
name: "id",
|
|
2955
|
+
requirement: "required",
|
|
2956
|
+
type: "string",
|
|
2957
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
2958
|
+
},
|
|
2959
|
+
{
|
|
2960
|
+
name: "order",
|
|
2961
|
+
requirement: "optional",
|
|
2962
|
+
type: "number",
|
|
2963
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
2964
|
+
},
|
|
2965
|
+
{
|
|
2966
|
+
name: "label",
|
|
2967
|
+
requirement: "optional",
|
|
2968
|
+
type: "string | (() => string)",
|
|
2969
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
2970
|
+
}
|
|
2971
|
+
],
|
|
2972
|
+
ownerProps: ["/** Header actions derive their state from the standard session/global kit. */\nexport interface ConversationHeaderActionOwnerProps {}"],
|
|
2973
|
+
ownerPropsReferences: [],
|
|
2974
|
+
standardProps: [
|
|
2975
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
2976
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
2977
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
2978
|
+
"sessionId: SessionId",
|
|
2979
|
+
"useProjection: UseProjection",
|
|
2980
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
2981
|
+
"inputActions: InputActions"
|
|
2982
|
+
],
|
|
2983
|
+
keyDomain: "",
|
|
2984
|
+
hookContext: "",
|
|
2985
|
+
slotInject: "",
|
|
2986
|
+
declaredBy: "an entry in 'conversation.session.header' (client-ui-conversation), so it exists while that entry is mounted",
|
|
2987
|
+
occupants: ["session-log-export SessionLogDownloadHeaderAction id 'session-log-download'"],
|
|
2988
|
+
replaceRisk: "none",
|
|
2989
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.session.header.utilities', () => ctx.slots.register(\n { name: 'conversation.session.header.utilities', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
2990
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:105"
|
|
2991
|
+
},
|
|
2992
|
+
{
|
|
2993
|
+
key: "conversation.view",
|
|
2994
|
+
kind: "list",
|
|
2995
|
+
scope: "session",
|
|
2996
|
+
summary: "The conversation view ring: one list entry per view tab (chat here; trajectory/waterfall from ui-trajectory), rendered one-at-a-time by the session body via `only: <active id>`.",
|
|
2997
|
+
doc: "The conversation view ring: one list entry per view tab (chat here;\ntrajectory/waterfall from ui-trajectory), rendered one-at-a-time by\nthe session body via `only: <active id>`. Declared by this package's\nbody entry (declaring is claiming). Session scope: views read the\nconversation snapshot through the standard kit.",
|
|
2998
|
+
registerOptions: [
|
|
2999
|
+
{
|
|
3000
|
+
name: "id",
|
|
3001
|
+
requirement: "required",
|
|
3002
|
+
type: "string",
|
|
3003
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
3004
|
+
},
|
|
3005
|
+
{
|
|
3006
|
+
name: "order",
|
|
3007
|
+
requirement: "optional",
|
|
3008
|
+
type: "number",
|
|
3009
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
3010
|
+
},
|
|
3011
|
+
{
|
|
3012
|
+
name: "label",
|
|
3013
|
+
requirement: "optional",
|
|
3014
|
+
type: "string | (() => string)",
|
|
3015
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
3016
|
+
}
|
|
3017
|
+
],
|
|
3018
|
+
ownerProps: ["/**\n * View-slot owner share: the cross-view inspect handoff (otherwise views need\n * nothing from the render site — sessionId and the snapshot hook arrive as\n * framework-standard props; tool rows go through each view's own declared\n * toolview hole).\n */\nexport interface ConvViewOwnerProps {\n /** One-shot inspect request from another view (chat's Inspect button); null when idle. */\n inspect?: { callId: CallId } | null\n /** Acknowledge the inspect request once applied (clears the store field). */\n onInspectDone?: () => void\n}"],
|
|
3019
|
+
ownerPropsReferences: [],
|
|
3020
|
+
standardProps: [
|
|
3021
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
3022
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
3023
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
3024
|
+
"sessionId: SessionId",
|
|
3025
|
+
"useProjection: UseProjection",
|
|
3026
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
3027
|
+
"inputActions: InputActions"
|
|
3028
|
+
],
|
|
3029
|
+
keyDomain: "",
|
|
3030
|
+
hookContext: "",
|
|
3031
|
+
slotInject: "",
|
|
3032
|
+
declaredBy: "an entry in 'conversation.session' (client-ui-conversation), so it exists while that entry is mounted",
|
|
3033
|
+
occupants: ["client-ui-conversation ChatView id 'chat'", "client-ui-trajectory TrajectoryView id 'trajectory'"],
|
|
3034
|
+
replaceRisk: "none",
|
|
3035
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('conversation.view', () => ctx.slots.register(\n { name: 'conversation.view', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3036
|
+
source: "packages/client/ui-conversation/src/client/contract/slots.ts:113"
|
|
3037
|
+
},
|
|
3038
|
+
{
|
|
3039
|
+
key: "details",
|
|
3040
|
+
kind: "single",
|
|
3041
|
+
scope: "session",
|
|
3042
|
+
summary: "The right details column, shown when the layout opens it.",
|
|
3043
|
+
doc: "The right details column, shown when the layout opens it. OCCUPIED by\nui-conversation's DetailsPanel, which declares the tool-details seat\ninside it — registering here replaces the column and takes that seat\nwith it. Absent an occupant the column renders nothing.\n\nNo owner props: the framework injects the session id and hooks for the\n`session` scope, and `ctx.layout` owns whether the column is open.",
|
|
3044
|
+
registerOptions: [],
|
|
3045
|
+
ownerProps: ["/** Details owner share: empty — sessionId arrives as a framework-standard prop. */\nexport interface DetailsOwnerProps {}"],
|
|
3046
|
+
ownerPropsReferences: [],
|
|
3047
|
+
standardProps: [
|
|
3048
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
3049
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
3050
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
3051
|
+
"sessionId: SessionId",
|
|
3052
|
+
"useProjection: UseProjection",
|
|
3053
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
3054
|
+
"inputActions: InputActions"
|
|
3055
|
+
],
|
|
3056
|
+
keyDomain: "",
|
|
3057
|
+
hookContext: "",
|
|
3058
|
+
slotInject: "",
|
|
3059
|
+
declaredBy: "an entry in 'root' (client-ui-layout), so it exists while that entry is mounted",
|
|
3060
|
+
occupants: ["client-ui-conversation DetailsPanel"],
|
|
3061
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3062
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('details', () => ctx.slots.register(\n { name: 'details' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3063
|
+
source: "packages/client/ui-layout/src/client/index.ts:72"
|
|
3064
|
+
},
|
|
3065
|
+
{
|
|
3066
|
+
key: "root",
|
|
3067
|
+
kind: "single",
|
|
3068
|
+
scope: "root",
|
|
3069
|
+
summary: "The built-in render-tree root hole (seeded by SlotCore): the one slot the shell itself renders, and the ancestor of every other seat.",
|
|
3070
|
+
doc: "The built-in render-tree root hole (seeded by SlotCore): the one slot the\nshell itself renders, and the ancestor of every other seat. OCCUPIED by\nui-layout's AppFrame, which declares the sidebar, conversation, details,\nand shell.overlay seats inside it.\n\nDO NOT register here. This is a single slot, so a second entry does not\nsit beside the frame — it shadows it, and a dynamically registered entry\nis assigned a lower priority than the shipped one, which makes it the\nwinner: the page would render your component alone, with every seat the\nframe declares gone. For a surface of your own that floats over the whole\napp, register into `shell.overlay` instead (a list slot: additive, and\nclick-through until your entry opts into pointer events).",
|
|
3071
|
+
registerOptions: [],
|
|
3072
|
+
ownerProps: ["/** Root owner share: the shell supplies nothing — the frame is inject-assembled. */\nexport interface RootOwnerProps { children?: never }"],
|
|
3073
|
+
ownerPropsReferences: [],
|
|
3074
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3075
|
+
keyDomain: "",
|
|
3076
|
+
hookContext: "",
|
|
3077
|
+
slotInject: "",
|
|
3078
|
+
declaredBy: "the runtime itself (built in; always present)",
|
|
3079
|
+
occupants: ["client-ui-layout AppFrame"],
|
|
3080
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3081
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('root', () => ctx.slots.register(\n { name: 'root' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3082
|
+
source: "packages/client/runtime/src/client/slots.ts:41"
|
|
3083
|
+
},
|
|
3084
|
+
{
|
|
3085
|
+
key: "settings.action",
|
|
3086
|
+
kind: "list",
|
|
3087
|
+
scope: "root",
|
|
3088
|
+
summary: "Optional actions rendered in the content-column header before Close.",
|
|
3089
|
+
doc: "Optional actions rendered in the content-column header before Close.\nRegistrants own visibility, behavior, copy, and failure presentation;\nthe shell supplies only the ordered render site.",
|
|
3090
|
+
registerOptions: [
|
|
3091
|
+
{
|
|
3092
|
+
name: "id",
|
|
3093
|
+
requirement: "required",
|
|
3094
|
+
type: "string",
|
|
3095
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
3096
|
+
},
|
|
3097
|
+
{
|
|
3098
|
+
name: "order",
|
|
3099
|
+
requirement: "optional",
|
|
3100
|
+
type: "number",
|
|
3101
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
3102
|
+
},
|
|
3103
|
+
{
|
|
3104
|
+
name: "label",
|
|
3105
|
+
requirement: "optional",
|
|
3106
|
+
type: "string | (() => string)",
|
|
3107
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
3108
|
+
}
|
|
3109
|
+
],
|
|
3110
|
+
ownerProps: ["/** Owner share of the header title seat (the shell supplies nothing). */\nexport interface SettingsHeaderOwnerProps {\n /** Marker field: header owner props are intentionally empty. */\n children?: never\n}"],
|
|
3111
|
+
ownerPropsReferences: [],
|
|
3112
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3113
|
+
keyDomain: "",
|
|
3114
|
+
hookContext: "",
|
|
3115
|
+
slotInject: "",
|
|
3116
|
+
declaredBy: "an entry in 'sidebar.settings' (client-ui-settings-general), so it exists while that entry is mounted",
|
|
3117
|
+
occupants: ["client-ui-settings-general SettingsDocumentAction id 'open-document'"],
|
|
3118
|
+
replaceRisk: "none",
|
|
3119
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.action', () => ctx.slots.register(\n { name: 'settings.action', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3120
|
+
source: "packages/client/ui-settings/src/client/contract/slots.ts:35"
|
|
3121
|
+
},
|
|
3122
|
+
{
|
|
3123
|
+
key: "settings.close",
|
|
3124
|
+
kind: "single",
|
|
3125
|
+
scope: "root",
|
|
3126
|
+
summary: "The close button's visually-hidden label text (the button itself — icon, geometry, focus — is shell chrome).",
|
|
3127
|
+
doc: "The close button's visually-hidden label text (the button itself —\nicon, geometry, focus — is shell chrome). Absent contribution leaves\nthe button without an accessible name (broken-composition state).",
|
|
3128
|
+
registerOptions: [],
|
|
3129
|
+
ownerProps: ["/** Owner share of the header title seat (the shell supplies nothing). */\nexport interface SettingsHeaderOwnerProps {\n /** Marker field: header owner props are intentionally empty. */\n children?: never\n}"],
|
|
3130
|
+
ownerPropsReferences: [],
|
|
3131
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3132
|
+
keyDomain: "",
|
|
3133
|
+
hookContext: "",
|
|
3134
|
+
slotInject: "",
|
|
3135
|
+
declaredBy: "an entry in 'sidebar.settings' (client-ui-settings-general), so it exists while that entry is mounted",
|
|
3136
|
+
occupants: ["client-ui-settings-general CloseLabel"],
|
|
3137
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3138
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.close', () => ctx.slots.register(\n { name: 'settings.close' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3139
|
+
source: "packages/client/ui-settings/src/client/contract/slots.ts:41"
|
|
3140
|
+
},
|
|
3141
|
+
{
|
|
3142
|
+
key: "settings.general.item",
|
|
3143
|
+
kind: "list",
|
|
3144
|
+
scope: "root",
|
|
3145
|
+
summary: "One preference row inside the General section — the additive seat for a single setting that needs no page of its own (a whole page is `settings.section`), contributed by the feature plugin that owns the preference (locale → Language, ui-theme → Appearance, ui-conversation → Composer Enter).",
|
|
3146
|
+
doc: "One preference row inside the General section — the additive seat for a\nsingle setting that needs no page of its own (a whole page is\n`settings.section`), contributed by the feature plugin that owns the\npreference (locale → Language, ui-theme → Appearance, ui-conversation →\nComposer Enter). Options: `id` (row key), `order` (row position). The\nsection column only stacks rows, so a row draws its own internals,\nincluding its label: nothing projects a `label` here and the owner passes\nno props at all — copy, current value, and the write path are all yours,\nthrough your own inject face and `host.call`. Declared at runtime by\nui-settings-general's General entry; the type lives here with every other\nsettings slot type, because this package is the settings domain's base\nlayer and every registrant already depends on it for `ctx.settingsScope`.",
|
|
3147
|
+
registerOptions: [
|
|
3148
|
+
{
|
|
3149
|
+
name: "id",
|
|
3150
|
+
requirement: "required",
|
|
3151
|
+
type: "string",
|
|
3152
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
3153
|
+
},
|
|
3154
|
+
{
|
|
3155
|
+
name: "order",
|
|
3156
|
+
requirement: "optional",
|
|
3157
|
+
type: "number",
|
|
3158
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
3159
|
+
},
|
|
3160
|
+
{
|
|
3161
|
+
name: "label",
|
|
3162
|
+
requirement: "optional",
|
|
3163
|
+
type: "string | (() => string)",
|
|
3164
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
3165
|
+
}
|
|
3166
|
+
],
|
|
3167
|
+
ownerProps: ["/** Owner share of a General preference row (the section supplies nothing). */\nexport interface SettingsGeneralItemOwnerProps {\n /** Marker field: item owner props are intentionally empty. */\n children?: never\n}"],
|
|
3168
|
+
ownerPropsReferences: [],
|
|
3169
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3170
|
+
keyDomain: "",
|
|
3171
|
+
hookContext: "",
|
|
3172
|
+
slotInject: "",
|
|
3173
|
+
declaredBy: "an entry in 'settings.section' (client-ui-settings-general), so it exists while that entry is mounted",
|
|
3174
|
+
occupants: [
|
|
3175
|
+
"client-locale LanguageRow id 'language'",
|
|
3176
|
+
"client-ui-agent-preset AgentPresetRow id 'agent-preset'",
|
|
3177
|
+
"client-ui-conversation EnterBehaviorRow id 'composer-enter'",
|
|
3178
|
+
"client-ui-permission-presets PermissionRow id 'permission'",
|
|
3179
|
+
"client-ui-theme AppearanceRow id 'appearance'"
|
|
3180
|
+
],
|
|
3181
|
+
replaceRisk: "none",
|
|
3182
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.general.item', () => ctx.slots.register(\n { name: 'settings.general.item', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3183
|
+
source: "packages/client/ui-settings/src/client/contract/slots.ts:88"
|
|
3184
|
+
},
|
|
3185
|
+
{
|
|
3186
|
+
key: "settings.header",
|
|
3187
|
+
kind: "single",
|
|
3188
|
+
scope: "root",
|
|
3189
|
+
summary: "The panel title text seat.",
|
|
3190
|
+
doc: "The panel title text seat. Content renders inside the nav heading row;\nthe dialog's accessible name points at that node via aria-labelledby.\nAbsent contribution leaves the heading empty.",
|
|
3191
|
+
registerOptions: [],
|
|
3192
|
+
ownerProps: ["/** Owner share of the header title seat (the shell supplies nothing). */\nexport interface SettingsHeaderOwnerProps {\n /** Marker field: header owner props are intentionally empty. */\n children?: never\n}"],
|
|
3193
|
+
ownerPropsReferences: [],
|
|
3194
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3195
|
+
keyDomain: "",
|
|
3196
|
+
hookContext: "",
|
|
3197
|
+
slotInject: "",
|
|
3198
|
+
declaredBy: "an entry in 'sidebar.settings' (client-ui-settings-general), so it exists while that entry is mounted",
|
|
3199
|
+
occupants: ["client-ui-settings-general HeaderContent"],
|
|
3200
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3201
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.header', () => ctx.slots.register(\n { name: 'settings.header' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3202
|
+
source: "packages/client/ui-settings/src/client/contract/slots.ts:29"
|
|
3203
|
+
},
|
|
3204
|
+
{
|
|
3205
|
+
key: "settings.onboarding",
|
|
3206
|
+
kind: "list",
|
|
3207
|
+
scope: "root",
|
|
3208
|
+
summary: "Root-scoped onboarding steps contributed by settings features.",
|
|
3209
|
+
doc: "Root-scoped onboarding steps contributed by settings features. The\nshell mounts one ordered step at a time; the active registrant either\ncompletes itself or keeps ownership until the user completes its sole\npath. Registrants own readiness, copy, dialog behavior, AND visible\nchrome: a step wraps its visible content in its modal surface (including\n`#root` inert ownership) and renders null while private facts are still\nloading. The shell paints no chrome of its own, so a mounted-but-deciding\nstep shows and blocks nothing.",
|
|
3210
|
+
registerOptions: [
|
|
3211
|
+
{
|
|
3212
|
+
name: "id",
|
|
3213
|
+
requirement: "required",
|
|
3214
|
+
type: "string",
|
|
3215
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
3216
|
+
},
|
|
3217
|
+
{
|
|
3218
|
+
name: "order",
|
|
3219
|
+
requirement: "optional",
|
|
3220
|
+
type: "number",
|
|
3221
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
3222
|
+
},
|
|
3223
|
+
{
|
|
3224
|
+
name: "label",
|
|
3225
|
+
requirement: "optional",
|
|
3226
|
+
type: "string | (() => string)",
|
|
3227
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
3228
|
+
}
|
|
3229
|
+
],
|
|
3230
|
+
ownerProps: ["/** Owner share of the currently active settings-backed onboarding step. */\nexport interface SettingsOnboardingOwnerProps {\n /** Stable id of the step currently selected by the coordinator. */\n stepId: string\n /** Complete or skip this step and transfer ownership to the next entry. */\n complete: () => void\n /** Open the settings panel directly on one registered section. */\n openSection: (id: string) => void\n}"],
|
|
3231
|
+
ownerPropsReferences: [],
|
|
3232
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3233
|
+
keyDomain: "",
|
|
3234
|
+
hookContext: "",
|
|
3235
|
+
slotInject: "",
|
|
3236
|
+
declaredBy: "an entry in 'sidebar.settings' (client-ui-settings-general), so it exists while that entry is mounted",
|
|
3237
|
+
occupants: ["client-ui-settings-models WelcomeNotice id 'welcome-notice'", "client-ui-settings-models DeepSeekOnboardingDialog id 'deepseek-official'"],
|
|
3238
|
+
replaceRisk: "none",
|
|
3239
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.onboarding', () => ctx.slots.register(\n { name: 'settings.onboarding', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3240
|
+
source: "packages/client/ui-settings/src/client/contract/slots.ts:73"
|
|
3241
|
+
},
|
|
3242
|
+
{
|
|
3243
|
+
key: "settings.plugin.item",
|
|
3244
|
+
kind: "keyed",
|
|
3245
|
+
scope: "root",
|
|
3246
|
+
summary: "One plugin's card inside the plugin configuration section (see module JSDoc).",
|
|
3247
|
+
doc: "One plugin's card inside the plugin configuration section (see module JSDoc).",
|
|
3248
|
+
registerOptions: [{
|
|
3249
|
+
name: "key",
|
|
3250
|
+
requirement: "required",
|
|
3251
|
+
type: "string",
|
|
3252
|
+
doc: "Your cell key: the entry renders where the owner dispatches this exact key. Registering an already-occupied key replaces that occupant."
|
|
3253
|
+
}],
|
|
3254
|
+
ownerProps: ["/** Owner share of a plugin card (the section supplies nothing). */\nexport interface SettingsPluginItemOwnerProps {\n /** Marker field: card owner props are intentionally empty. */\n children?: never\n}"],
|
|
3255
|
+
ownerPropsReferences: [],
|
|
3256
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3257
|
+
keyDomain: "open: any string the owner dispatches (no compile-time key set), none are taken yet",
|
|
3258
|
+
hookContext: "",
|
|
3259
|
+
slotInject: "",
|
|
3260
|
+
declaredBy: "an entry in 'settings.plugins.tab' (client-ui-settings-plugins), so it exists while that entry is mounted",
|
|
3261
|
+
occupants: [
|
|
3262
|
+
"client-ui-settings-plugins BashCard",
|
|
3263
|
+
"client-ui-settings-plugins AgentLoopCard",
|
|
3264
|
+
"client-ui-settings-plugins WebSearchCard"
|
|
3265
|
+
],
|
|
3266
|
+
replaceRisk: "none",
|
|
3267
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register(\n { name: 'settings.plugin.item', key: '<one key the owner dispatches>' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3268
|
+
source: "packages/client/ui-settings-plugins/src/client/slot-contract.ts:19"
|
|
3269
|
+
},
|
|
3270
|
+
{
|
|
3271
|
+
key: "settings.plugins.tab",
|
|
3272
|
+
kind: "list",
|
|
3273
|
+
scope: "root",
|
|
3274
|
+
summary: "One page inside the Plugins settings section.",
|
|
3275
|
+
doc: "One page inside the Plugins settings section. The section owner renders\nlocalized entry labels as tabs and mounts each contribution inside its\ncorresponding tab panel. Options: `id` (tab key), `order` (tab order),\nand `label` (registrant-localized tab text). Declared at runtime by the\nfeature that owns the Plugins section; the type lives here so inventory\nand configuration plugins collaborate without depending on one another.",
|
|
3276
|
+
registerOptions: [
|
|
3277
|
+
{
|
|
3278
|
+
name: "id",
|
|
3279
|
+
requirement: "required",
|
|
3280
|
+
type: "string",
|
|
3281
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
3282
|
+
},
|
|
3283
|
+
{
|
|
3284
|
+
name: "order",
|
|
3285
|
+
requirement: "optional",
|
|
3286
|
+
type: "number",
|
|
3287
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
3288
|
+
},
|
|
3289
|
+
{
|
|
3290
|
+
name: "label",
|
|
3291
|
+
requirement: "optional",
|
|
3292
|
+
type: "string | (() => string)",
|
|
3293
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
3294
|
+
}
|
|
3295
|
+
],
|
|
3296
|
+
ownerProps: ["/** Owner share of a Plugins tab (the section supplies nothing). */\nexport interface SettingsPluginsTabOwnerProps {\n /** Marker field: tab owner props are intentionally empty. */\n children?: never\n}"],
|
|
3297
|
+
ownerPropsReferences: [],
|
|
3298
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3299
|
+
keyDomain: "",
|
|
3300
|
+
hookContext: "",
|
|
3301
|
+
slotInject: "",
|
|
3302
|
+
declaredBy: "an entry in 'settings.section' (client-ui-settings-plugins), so it exists while that entry is mounted",
|
|
3303
|
+
occupants: ["client-ui-settings-plugin-inventory PluginInventorySettingsTab id 'all'", "client-ui-settings-plugins ConfigurablePluginsTab id 'configurable'"],
|
|
3304
|
+
replaceRisk: "none",
|
|
3305
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.plugins.tab', () => ctx.slots.register(\n { name: 'settings.plugins.tab', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3306
|
+
source: "packages/client/ui-settings/src/client/contract/slots.ts:62"
|
|
3307
|
+
},
|
|
3308
|
+
{
|
|
3309
|
+
key: "settings.section",
|
|
3310
|
+
kind: "list",
|
|
3311
|
+
scope: "root",
|
|
3312
|
+
summary: "One settings page per list entry.",
|
|
3313
|
+
doc: "One settings page per list entry. Registrant options carry the nav\nidentity: `id` (section key, drives `only` filtering), `order` (nav\nposition), `label` (registrant-localized display text — the registrant\nre-registers with fresh text on locale change, so the shell never\nsubscribes locale state; the ledger bump doubles as the shell's\nre-render trigger). Sections render inside the panel content column.\n(`settings.general.item`, declared by ui-settings-general's General\nentry, is typed in the locale package — the common dependency of every\nitem registrant; the shell neither declares nor renders it.)",
|
|
3314
|
+
registerOptions: [
|
|
3315
|
+
{
|
|
3316
|
+
name: "id",
|
|
3317
|
+
requirement: "required",
|
|
3318
|
+
type: "string",
|
|
3319
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
3320
|
+
},
|
|
3321
|
+
{
|
|
3322
|
+
name: "order",
|
|
3323
|
+
requirement: "optional",
|
|
3324
|
+
type: "number",
|
|
3325
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
3326
|
+
},
|
|
3327
|
+
{
|
|
3328
|
+
name: "label",
|
|
3329
|
+
requirement: "optional",
|
|
3330
|
+
type: "string | (() => string)",
|
|
3331
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
3332
|
+
}
|
|
3333
|
+
],
|
|
3334
|
+
ownerProps: ["/**\n * Owner share of a settings section entry. The shell owns modal visibility\n * and navigation; a section's data arrives through its own inject faces and\n * stores. `close` is the one shell affordance a section receives, for flows\n * that leave settings altogether (starting a session from a section) — the\n * onboarding coordinator's `openSection`/`complete` precedent, inverted.\n */\nexport interface SettingsSectionOwnerProps {\n /** Close the settings panel (the shell owns the open state). */\n close: () => void\n}"],
|
|
3335
|
+
ownerPropsReferences: [],
|
|
3336
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3337
|
+
keyDomain: "",
|
|
3338
|
+
hookContext: "",
|
|
3339
|
+
slotInject: "",
|
|
3340
|
+
declaredBy: "an entry in 'sidebar.settings' (client-ui-settings-general), so it exists while that entry is mounted",
|
|
3341
|
+
occupants: [
|
|
3342
|
+
"client-ui-agent-preset AgentPresetSection id 'agent-presets'",
|
|
3343
|
+
"client-ui-settings-general GeneralSection id 'general'",
|
|
3344
|
+
"client-ui-settings-models ModelsSection id 'models'",
|
|
3345
|
+
"client-ui-settings-plugins PluginsSettingsSection id 'plugins'"
|
|
3346
|
+
],
|
|
3347
|
+
replaceRisk: "none",
|
|
3348
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n { name: 'settings.section', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3349
|
+
source: "packages/client/ui-settings/src/client/contract/slots.ts:53"
|
|
3350
|
+
},
|
|
3351
|
+
{
|
|
3352
|
+
key: "settings.trigger",
|
|
3353
|
+
kind: "single",
|
|
3354
|
+
scope: "root",
|
|
3355
|
+
summary: "The sidebar-foot trigger row content: icon + label, supplied as slot content (the accessible name comes from the content — rail state renders the label visually hidden).",
|
|
3356
|
+
doc: "The sidebar-foot trigger row content: icon + label, supplied as slot\ncontent (the accessible name comes from the content — rail state\nrenders the label visually hidden). The shell renders the button\nchrome and owns open state. Absent contribution degrades to an\nicon-only button without an accessible name (broken-composition state;\nthe shipped composition always registers the seat).",
|
|
3357
|
+
registerOptions: [],
|
|
3358
|
+
ownerProps: ["/** Owner share of the trigger content seat: the sidebar column state. */\nexport interface SettingsTriggerOwnerProps {\n /** Whether the sidebar renders wide content (false = 56px rail, icon only). */\n wide: boolean\n}"],
|
|
3359
|
+
ownerPropsReferences: [],
|
|
3360
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3361
|
+
keyDomain: "",
|
|
3362
|
+
hookContext: "",
|
|
3363
|
+
slotInject: "",
|
|
3364
|
+
declaredBy: "an entry in 'sidebar.settings' (client-ui-settings-general), so it exists while that entry is mounted",
|
|
3365
|
+
occupants: ["client-ui-settings-general TriggerContent"],
|
|
3366
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3367
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('settings.trigger', () => ctx.slots.register(\n { name: 'settings.trigger' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3368
|
+
source: "packages/client/ui-settings/src/client/contract/slots.ts:23"
|
|
3369
|
+
},
|
|
3370
|
+
{
|
|
3371
|
+
key: "shell.overlay",
|
|
3372
|
+
kind: "list",
|
|
3373
|
+
scope: "root",
|
|
3374
|
+
summary: "Frame-wide floating layer, above every column and outside their scroll containers.",
|
|
3375
|
+
doc: "Frame-wide floating layer, above every column and outside their scroll\ncontainers. Deliberately generic and unowned by any feature: a badge, a\ntoast stack or a status pill all belong here, and entries order among\nthemselves. The layer itself is click-through — entries opt back into\npointer events — so an occupant never blocks the app underneath.\n\nThis is the additive seat for a frame-wide surface of your own: a fresh\n`id` is added beside the shipped entries instead of replacing them.",
|
|
3376
|
+
registerOptions: [
|
|
3377
|
+
{
|
|
3378
|
+
name: "id",
|
|
3379
|
+
requirement: "required",
|
|
3380
|
+
type: "string",
|
|
3381
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
3382
|
+
},
|
|
3383
|
+
{
|
|
3384
|
+
name: "order",
|
|
3385
|
+
requirement: "optional",
|
|
3386
|
+
type: "number",
|
|
3387
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
3388
|
+
},
|
|
3389
|
+
{
|
|
3390
|
+
name: "label",
|
|
3391
|
+
requirement: "optional",
|
|
3392
|
+
type: "string | (() => string)",
|
|
3393
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
3394
|
+
}
|
|
3395
|
+
],
|
|
3396
|
+
ownerProps: [],
|
|
3397
|
+
ownerPropsReferences: [],
|
|
3398
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3399
|
+
keyDomain: "",
|
|
3400
|
+
hookContext: "",
|
|
3401
|
+
slotInject: "",
|
|
3402
|
+
declaredBy: "an entry in 'root' (client-ui-layout), so it exists while that entry is mounted",
|
|
3403
|
+
occupants: [],
|
|
3404
|
+
replaceRisk: "none",
|
|
3405
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('shell.overlay', () => ctx.slots.register(\n { name: 'shell.overlay', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3406
|
+
source: "packages/client/ui-layout/src/client/index.ts:83"
|
|
3407
|
+
},
|
|
3408
|
+
{
|
|
3409
|
+
key: "sidebar",
|
|
3410
|
+
kind: "single",
|
|
3411
|
+
scope: "root",
|
|
3412
|
+
summary: "The whole left column.",
|
|
3413
|
+
doc: "The whole left column. OCCUPIED by ui-sidebar's SidebarRoot, which\ndeclares the workspace and settings seats inside it — registering here\nreplaces the navigation column outright rather than adding to it, and\nthe seats it declares disappear with it. To add something to the\nsidebar, register into one of those inner seats instead.\n\nThe occupant receives the frame's live column state (collapsed, width)\nand is expected to render the compact control rail while collapsed.",
|
|
3414
|
+
registerOptions: [],
|
|
3415
|
+
ownerProps: ["/** Sidebar owner share: live column state from the frame's concession solve. */\nexport interface SidebarOwnerProps {\n /** True when the sidebar is closed (the column renders the compact control rail). */\n collapsed: boolean\n /** Rendered column width in px (SIDEBAR_COLLAPSED when collapsed). */\n width: number\n}"],
|
|
3416
|
+
ownerPropsReferences: [],
|
|
3417
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3418
|
+
keyDomain: "",
|
|
3419
|
+
hookContext: "",
|
|
3420
|
+
slotInject: "",
|
|
3421
|
+
declaredBy: "an entry in 'root' (client-ui-layout), so it exists while that entry is mounted",
|
|
3422
|
+
occupants: ["client-ui-sidebar SidebarRoot"],
|
|
3423
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3424
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('sidebar', () => ctx.slots.register(\n { name: 'sidebar' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3425
|
+
source: "packages/client/ui-layout/src/client/index.ts:49"
|
|
3426
|
+
},
|
|
3427
|
+
{
|
|
3428
|
+
key: "sidebar.brand.mark",
|
|
3429
|
+
kind: "single",
|
|
3430
|
+
scope: "root",
|
|
3431
|
+
summary: "Brand mark rendered in the expanded brand row and collapsed rail.",
|
|
3432
|
+
doc: "Brand mark rendered in the expanded brand row and collapsed rail.\nDeclared by this package's `sidebar` entry; deployments may replace\nthe shell's fish fallback without replacing the surrounding controls.",
|
|
3433
|
+
registerOptions: [],
|
|
3434
|
+
ownerProps: ["/** Geometry supplied to the sidebar brand-mark occupant. */\nexport interface SidebarBrandMarkOwnerProps {\n /** Requested square edge in pixels. */\n size: number\n}"],
|
|
3435
|
+
ownerPropsReferences: [],
|
|
3436
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3437
|
+
keyDomain: "",
|
|
3438
|
+
hookContext: "",
|
|
3439
|
+
slotInject: "",
|
|
3440
|
+
declaredBy: "an entry in 'sidebar' (client-ui-sidebar), so it exists while that entry is mounted",
|
|
3441
|
+
occupants: ["client-ui-brand-official OfficialBrandMark"],
|
|
3442
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3443
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('sidebar.brand.mark', () => ctx.slots.register(\n { name: 'sidebar.brand.mark' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3444
|
+
source: "packages/client/ui-sidebar/src/client/contract/slots.ts:23"
|
|
3445
|
+
},
|
|
3446
|
+
{
|
|
3447
|
+
key: "sidebar.brand.name",
|
|
3448
|
+
kind: "single",
|
|
3449
|
+
scope: "root",
|
|
3450
|
+
summary: "Brand name rendered beside the expanded mark.",
|
|
3451
|
+
doc: "Brand name rendered beside the expanded mark. Declared by this\npackage's `sidebar` entry; the shell supplies a generic text fallback.",
|
|
3452
|
+
registerOptions: [],
|
|
3453
|
+
ownerProps: ["/** Empty owner share for the sidebar brand-name occupant. */\nexport interface SidebarBrandNameOwnerProps {\n /** Marker field: the occupant owns its own content and width. */\n children?: never\n}"],
|
|
3454
|
+
ownerPropsReferences: [],
|
|
3455
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3456
|
+
keyDomain: "",
|
|
3457
|
+
hookContext: "",
|
|
3458
|
+
slotInject: "",
|
|
3459
|
+
declaredBy: "an entry in 'sidebar' (client-ui-sidebar), so it exists while that entry is mounted",
|
|
3460
|
+
occupants: ["client-ui-brand-official OfficialBrandName"],
|
|
3461
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3462
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('sidebar.brand.name', () => ctx.slots.register(\n { name: 'sidebar.brand.name' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3463
|
+
source: "packages/client/ui-sidebar/src/client/contract/slots.ts:28"
|
|
3464
|
+
},
|
|
3465
|
+
{
|
|
3466
|
+
key: "sidebar.footer.action",
|
|
3467
|
+
kind: "list",
|
|
3468
|
+
scope: "root",
|
|
3469
|
+
summary: "Optional actions beside Settings at the sidebar foot.",
|
|
3470
|
+
doc: "Optional actions beside Settings at the sidebar foot. Declared by this\npackage's 'sidebar' entry; each action receives only the column state.",
|
|
3471
|
+
registerOptions: [
|
|
3472
|
+
{
|
|
3473
|
+
name: "id",
|
|
3474
|
+
requirement: "required",
|
|
3475
|
+
type: "string",
|
|
3476
|
+
doc: "Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it."
|
|
3477
|
+
},
|
|
3478
|
+
{
|
|
3479
|
+
name: "order",
|
|
3480
|
+
requirement: "optional",
|
|
3481
|
+
type: "number",
|
|
3482
|
+
doc: "Position among the entries, ascending (default 0)."
|
|
3483
|
+
},
|
|
3484
|
+
{
|
|
3485
|
+
name: "label",
|
|
3486
|
+
requirement: "optional",
|
|
3487
|
+
type: "string | (() => string)",
|
|
3488
|
+
doc: "Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering."
|
|
3489
|
+
}
|
|
3490
|
+
],
|
|
3491
|
+
ownerProps: ["/** Owner share of an action rendered beside Settings at the sidebar foot. */\nexport interface SidebarFooterActionOwnerProps {\n /** Whether the sidebar renders wide content (false = 56px rail). */\n wide: boolean\n}"],
|
|
3492
|
+
ownerPropsReferences: [],
|
|
3493
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3494
|
+
keyDomain: "",
|
|
3495
|
+
hookContext: "",
|
|
3496
|
+
slotInject: "",
|
|
3497
|
+
declaredBy: "an entry in 'sidebar' (client-ui-sidebar), so it exists while that entry is mounted",
|
|
3498
|
+
occupants: ["client-ui-cordis CordisPanel id 'cordis-panel'"],
|
|
3499
|
+
replaceRisk: "none",
|
|
3500
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register(\n { name: 'sidebar.footer.action', id: 'my-entry', order: 100, label: 'My entry' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3501
|
+
source: "packages/client/ui-sidebar/src/client/contract/slots.ts:46"
|
|
3502
|
+
},
|
|
3503
|
+
{
|
|
3504
|
+
key: "sidebar.settings",
|
|
3505
|
+
kind: "single",
|
|
3506
|
+
scope: "root",
|
|
3507
|
+
summary: "The settings seat at the sidebar foot.",
|
|
3508
|
+
doc: "The settings seat at the sidebar foot. Declared by this package's\n'sidebar' entry; ui-settings registers its trigger row + modal panel.\nThe sidebar passes only its column state — it holds no settings state.",
|
|
3509
|
+
registerOptions: [],
|
|
3510
|
+
ownerProps: ["/**\n * Owner share of the sidebar settings seat: the column display state the\n * occupant's trigger row must render against (wide row vs rail icon).\n */\nexport interface SidebarSettingsOwnerProps {\n /** Whether the sidebar renders wide content (false = 56px rail). */\n wide: boolean\n}"],
|
|
3511
|
+
ownerPropsReferences: [],
|
|
3512
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3513
|
+
keyDomain: "",
|
|
3514
|
+
hookContext: "",
|
|
3515
|
+
slotInject: "",
|
|
3516
|
+
declaredBy: "an entry in 'sidebar' (client-ui-sidebar), so it exists while that entry is mounted",
|
|
3517
|
+
occupants: ["client-ui-settings-general SettingsRoot"],
|
|
3518
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3519
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('sidebar.settings', () => ctx.slots.register(\n { name: 'sidebar.settings' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3520
|
+
source: "packages/client/ui-sidebar/src/client/contract/slots.ts:41"
|
|
3521
|
+
},
|
|
3522
|
+
{
|
|
3523
|
+
key: "sidebar.workspaces",
|
|
3524
|
+
kind: "single",
|
|
3525
|
+
scope: "root",
|
|
3526
|
+
summary: "The workspace/session browsing region: section header, search, the grouped/flat session list, and every workspace dialog.",
|
|
3527
|
+
doc: "The workspace/session browsing region: section header, search, the\ngrouped/flat session list, and every workspace dialog. Declared by this\npackage's 'sidebar' entry (declaring is claiming); ui-workspace\nregisters the browser.",
|
|
3528
|
+
registerOptions: [],
|
|
3529
|
+
ownerProps: ["/**\n * Owner share of the browser hole — the only facts crossing the shell/region\n * boundary. Business data and actions arrive through the region's own inject.\n */\nexport interface SidebarSectionOwnerProps {\n /** Shell fold-state output: wide renders the full browser, rail the icon column. */\n wide: boolean\n /** Rail icons request expansion; the browser rides the wide flip for focus. */\n expandSidebar: () => void\n}"],
|
|
3530
|
+
ownerPropsReferences: [],
|
|
3531
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3532
|
+
keyDomain: "",
|
|
3533
|
+
hookContext: "",
|
|
3534
|
+
slotInject: "",
|
|
3535
|
+
declaredBy: "an entry in 'sidebar' (client-ui-sidebar), so it exists while that entry is mounted",
|
|
3536
|
+
occupants: ["client-ui-workspace WorkspaceBrowser"],
|
|
3537
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3538
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('sidebar.workspaces', () => ctx.slots.register(\n { name: 'sidebar.workspaces' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3539
|
+
source: "packages/client/ui-sidebar/src/client/contract/slots.ts:35"
|
|
3540
|
+
},
|
|
3541
|
+
{
|
|
3542
|
+
key: "sidebar.workspaces.directoryFlow",
|
|
3543
|
+
kind: "single",
|
|
3544
|
+
scope: "root",
|
|
3545
|
+
summary: "Directory-flow hole under the sidebar browsing region (declared by the WorkspaceBrowser entry).",
|
|
3546
|
+
doc: "Directory-flow hole under the sidebar browsing region (declared by the WorkspaceBrowser entry).",
|
|
3547
|
+
registerOptions: [],
|
|
3548
|
+
ownerProps: ["/**\n * Owner share of the directory-flow holes: the complete conversation between\n * the trigger surface and the picking interaction. The occupant reads `open`\n * to run/render its interaction and reports exactly one outcome per open.\n */\nexport interface DirectoryFlowOwnerProps {\n /** True while a picking interaction is requested; flipping back to false withdraws the request. */\n open: boolean\n /** True while the owner adopts a picked path (`createWorkspace` in flight); occupants disable their commit affordances. */\n busy: boolean\n /** The operator picked a directory (absolute host path); the owner adopts it. */\n onPicked: (path: string) => void\n /** The operator dismissed the interaction; the owner just closes the flow. */\n onCancel: () => void\n /** The interaction itself failed (chooser missing, listing denied); the owner shows its error surface. */\n onError: (message: string) => void\n}"],
|
|
3549
|
+
ownerPropsReferences: [],
|
|
3550
|
+
standardProps: ["useSessions: SnapshotSelectorHook<SessionListState>", "useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>"],
|
|
3551
|
+
keyDomain: "",
|
|
3552
|
+
hookContext: "",
|
|
3553
|
+
slotInject: "",
|
|
3554
|
+
declaredBy: "an entry in 'sidebar.workspaces' (client-ui-workspace), so it exists while that entry is mounted",
|
|
3555
|
+
occupants: ["client-ui-directory-picker-browse BrowseDirectoryFlow", "client-ui-directory-picker-native NativeDirectoryFlow"],
|
|
3556
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3557
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('sidebar.workspaces.directoryFlow', () => ctx.slots.register(\n { name: 'sidebar.workspaces.directoryFlow' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3558
|
+
source: "packages/client/ui-workspace/src/client/contract/slots.ts:59"
|
|
3559
|
+
},
|
|
3560
|
+
{
|
|
3561
|
+
key: "tool.call.toolview",
|
|
3562
|
+
kind: "keyed",
|
|
3563
|
+
scope: "session",
|
|
3564
|
+
summary: "Keyed atomic Tool call view, dispatched by the wire Tool name.",
|
|
3565
|
+
doc: "Keyed atomic Tool call view, dispatched by the wire Tool name. Register\nwith `key: '<tool name>'` to own how one tool's calls render inside a\nturn — the key domain is open (any wire tool name, including a tool your\nown package registered), so there is no compile-time key set to pick\nfrom and a typo simply never renders.\n\nA key the shipped composition already covers is replaced, not shared;\nan unclaimed key falls back to the generic tool row, so registering is\nadditive for your own tool and a takeover for a shipped one. The owner\npasses the call's identity, its frozen running-or-settled node, and the\nexpansion state (see ToolCallOwnerProps), so the view stays a pure\nfunction of what the turn already knows.",
|
|
3566
|
+
registerOptions: [{
|
|
3567
|
+
name: "key",
|
|
3568
|
+
requirement: "required",
|
|
3569
|
+
type: "string",
|
|
3570
|
+
doc: "Your cell key: the entry renders where the owner dispatches this exact key. Registering an already-occupied key replaces that occupant."
|
|
3571
|
+
}],
|
|
3572
|
+
ownerProps: ["/** Standard owner currency supplied to every atomic Tool view. */\nexport interface ToolCallOwnerProps {\n /** Tool call identity, stable across running and settled forms. */\n callId: string\n /** Wire Tool name and keyed dispatch value. */\n toolName: string\n /** Frozen running call or settled result node. */\n block: ToolCallBlock\n /** Session workspace root for relative summaries. */\n cwd?: string | undefined\n /** Host account home; POSIX home-rooted summaries display as `~`. */\n home?: string | undefined\n /** Open a Tool argument path through the Host. */\n openFile: (path: string) => void\n /** Inspect this call in the trajectory view when available. */\n inspect?: (() => void) | undefined\n}"],
|
|
3573
|
+
ownerPropsReferences: ["Wire"],
|
|
3574
|
+
standardProps: [
|
|
3575
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
3576
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
3577
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
3578
|
+
"sessionId: SessionId",
|
|
3579
|
+
"useProjection: UseProjection",
|
|
3580
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
3581
|
+
"inputActions: InputActions"
|
|
3582
|
+
],
|
|
3583
|
+
keyDomain: "open: any string the owner dispatches (no compile-time key set), already taken: ask_user_question, bash, cordis_define, cordis_run, cordis_stop, cordis_undefine, edit, glob, grep, read, skill, todo_write, web_fetch, web_search, write",
|
|
3584
|
+
hookContext: "",
|
|
3585
|
+
slotInject: "",
|
|
3586
|
+
declaredBy: "an entry in 'conversation.chat.node' (client-ui-tool), so it exists while that entry is mounted",
|
|
3587
|
+
occupants: [
|
|
3588
|
+
"client-ui-skill SkillRow key 'skill'",
|
|
3589
|
+
"client-ui-tool AskQuestionRow key 'ask_user_question'",
|
|
3590
|
+
"client-ui-tool BashRow key 'bash'",
|
|
3591
|
+
"client-ui-tool FileMutationRow key 'edit'",
|
|
3592
|
+
"client-ui-tool FileMutationRow key 'write'",
|
|
3593
|
+
"client-ui-tool ReadRow key 'read'",
|
|
3594
|
+
"client-ui-tool SearchRow key 'grep'",
|
|
3595
|
+
"client-ui-tool SearchRow key 'glob'",
|
|
3596
|
+
"client-ui-tool TodoRow key 'todo_write'",
|
|
3597
|
+
"client-ui-tool WebRow key 'web_search'",
|
|
3598
|
+
"client-ui-tool WebRow key 'web_fetch'",
|
|
3599
|
+
"client-ui-cordis CordisDefineRow key 'cordis_define'",
|
|
3600
|
+
"client-ui-cordis CordisRunRow key 'cordis_run'",
|
|
3601
|
+
"client-ui-cordis CordisActionRow key 'cordis_stop'",
|
|
3602
|
+
"client-ui-cordis CordisActionRow key 'cordis_undefine'"
|
|
3603
|
+
],
|
|
3604
|
+
replaceRisk: "shadows-shipped-ui",
|
|
3605
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register(\n { name: 'tool.call.toolview', key: '<one key the owner dispatches>' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3606
|
+
source: "packages/client/ui-tool/src/client/contract/slots.ts:24"
|
|
3607
|
+
},
|
|
3608
|
+
{
|
|
3609
|
+
key: "tool.view.cordis",
|
|
3610
|
+
kind: "keyed",
|
|
3611
|
+
scope: "session",
|
|
3612
|
+
summary: "Interactive Package-owned region rendered inside the latest eligible `cordis_run` card in the conversation flow.",
|
|
3613
|
+
doc: "Interactive Package-owned region rendered inside the latest eligible\n`cordis_run` card in the conversation flow. Use it for controls and other\nUI the user can interact with. Dynamic Client code registers with\n`key: 'self'`; the Guard binds that key to the current Plugin and Package.",
|
|
3614
|
+
registerOptions: [{
|
|
3615
|
+
name: "key",
|
|
3616
|
+
requirement: "required",
|
|
3617
|
+
type: "string",
|
|
3618
|
+
doc: "Your cell key: the entry renders where the owner dispatches this exact key. Registering an already-occupied key replaces that occupant."
|
|
3619
|
+
}],
|
|
3620
|
+
ownerProps: ["/** Owner currency delivered to a dynamic Package's business view. */\nexport interface CordisToolViewOwnerProps {\n readonly pluginId: CordisDynamicPluginId\n readonly packageId: CordisDynamicPackageId\n readonly pluginRunId: CordisDynamicPluginRunId\n}"],
|
|
3621
|
+
ownerPropsReferences: [
|
|
3622
|
+
"CordisDynamicPackageId",
|
|
3623
|
+
"CordisDynamicPluginId",
|
|
3624
|
+
"CordisDynamicPluginRunId"
|
|
3625
|
+
],
|
|
3626
|
+
standardProps: [
|
|
3627
|
+
"useSessions: SnapshotSelectorHook<SessionListState>",
|
|
3628
|
+
"useWorkspaces: SnapshotSelectorHook<import('./workspaces/service.ts').WorkspaceListState>",
|
|
3629
|
+
"useSession: SnapshotSelectorHook<ConversationSnapshot>",
|
|
3630
|
+
"sessionId: SessionId",
|
|
3631
|
+
"useProjection: UseProjection",
|
|
3632
|
+
"useInput: SnapshotSelectorHook<InputState>",
|
|
3633
|
+
"inputActions: InputActions"
|
|
3634
|
+
],
|
|
3635
|
+
keyDomain: "open: any string the owner dispatches (no compile-time key set), none are taken yet",
|
|
3636
|
+
hookContext: "",
|
|
3637
|
+
slotInject: "",
|
|
3638
|
+
declaredBy: "an entry in 'tool.call.toolview' (client-ui-cordis), so it exists while that entry is mounted",
|
|
3639
|
+
occupants: [],
|
|
3640
|
+
replaceRisk: "none",
|
|
3641
|
+
example: "return {\n inject: ['slots'],\n apply(ctx) {\n ctx.slots.inject('tool.view.cordis', () => ctx.slots.register(\n { name: 'tool.view.cordis', key: '<one key the owner dispatches>' },\n () => React.createElement('div', null, 'hello'),\n ))\n },\n}",
|
|
3642
|
+
source: "packages/extensions/ui-cordis/src/client/slots.ts:31"
|
|
3643
|
+
}
|
|
3644
|
+
];
|
|
3645
|
+
//#endregion
|
|
3646
|
+
//#region lib/types/client/providers.js
|
|
3647
|
+
/** Built-in Client inspect providers over live Client-owned services. */
|
|
3648
|
+
const EMPTY_INPUT = {
|
|
3649
|
+
type: "object",
|
|
3650
|
+
properties: {},
|
|
3651
|
+
additionalProperties: false
|
|
3652
|
+
};
|
|
3653
|
+
const ANY_OUTPUT = { description: "JSON data owned by this inspect provider." };
|
|
3654
|
+
const SERVICE_INPUT = exactInput("service", "Exact Service key. Omit it for the compact Service and method-signature directory.");
|
|
3655
|
+
const EVENT_INPUT = exactInput("event", "Exact Event name. Omit it for the compact Event and listener-signature directory.");
|
|
3656
|
+
const SERVICE_OUTPUT = { description: "Compact Service directory, or one exact Service contract with only its referenced type declarations." };
|
|
3657
|
+
const EVENT_OUTPUT = { description: "Compact Event directory, or one exact Event contract with only its referenced type declarations." };
|
|
3658
|
+
const SUBTREE_OUTPUT = { description: "Compact purpose/topology trees. With root, selected also contains that Slot's full contract and live occupants." };
|
|
3659
|
+
const SUBTREE_INPUT = {
|
|
3660
|
+
type: "object",
|
|
3661
|
+
properties: { root: {
|
|
3662
|
+
type: "string",
|
|
3663
|
+
description: "Exact live Slot key. When supplied, selected contains the full contract for this Slot."
|
|
3664
|
+
} },
|
|
3665
|
+
additionalProperties: false
|
|
3666
|
+
};
|
|
3667
|
+
/** Exact Client closure symbols exposed by the evaluator and guard. */
|
|
3668
|
+
const CLIENT_BUILTIN_INSPECTION = [
|
|
3669
|
+
{
|
|
3670
|
+
name: "ctx",
|
|
3671
|
+
description: "Restricted Cordis Context. Prefer ctx.get(name) with an undefined check; use inject only for hard dependencies.",
|
|
3672
|
+
signatures: [
|
|
3673
|
+
"ctx.get(name: string): unknown | undefined",
|
|
3674
|
+
"ctx.on(name: string, listener: Function): () => void",
|
|
3675
|
+
"ctx.provide(name: string, value: unknown): () => void",
|
|
3676
|
+
"ctx.effect(callback: Function, label?: string): () => void"
|
|
3677
|
+
]
|
|
3678
|
+
},
|
|
3679
|
+
{
|
|
3680
|
+
name: "React",
|
|
3681
|
+
description: "React runtime exposed without JSX transformation.",
|
|
3682
|
+
signatures: [
|
|
3683
|
+
"React.createElement(type, props, ...children): ReactElement",
|
|
3684
|
+
"React.useState(initial)",
|
|
3685
|
+
"React.useEffect(effect, deps)"
|
|
3686
|
+
]
|
|
3687
|
+
},
|
|
3688
|
+
{
|
|
3689
|
+
name: "host",
|
|
3690
|
+
description: "Package-private JSON RPC from Client to this Package's Host half.",
|
|
3691
|
+
signatures: ["host.call(method: string, args?: JsonValue): Promise<JsonValue>"]
|
|
3692
|
+
},
|
|
3693
|
+
{
|
|
3694
|
+
name: "styles",
|
|
3695
|
+
description: "Package-owned stylesheet insertion cleaned up with the Client run.",
|
|
3696
|
+
signatures: ["styles.insert(css: string): () => void"]
|
|
3697
|
+
},
|
|
3698
|
+
{
|
|
3699
|
+
name: "console",
|
|
3700
|
+
description: "Package-tagged browser logging.",
|
|
3701
|
+
signatures: ["console.log(...values): void", "console.error(...values): void"]
|
|
3702
|
+
}
|
|
3703
|
+
];
|
|
3704
|
+
/**
|
|
3705
|
+
* Construct the first-party Client provider registrations.
|
|
3706
|
+
* @param ctx - Client context used for live Service-backed queries.
|
|
3707
|
+
* @returns registrations for static catalogs and live Client capabilities.
|
|
3708
|
+
*/
|
|
3709
|
+
function clientInspectProviders(ctx) {
|
|
3710
|
+
return [
|
|
3711
|
+
registration("Service", "Progressive Client Service discovery: compact capability/signature directory, then one exact coding contract.", "listService", (input) => queryServiceApi(readExact(input, "service")), SERVICE_INPUT, SERVICE_OUTPUT),
|
|
3712
|
+
registration("Event", "Progressive Client Event discovery: compact listener directory, then one exact event contract.", "listEvents", (input) => queryEventApi(readExact(input, "event")), EVENT_INPUT, EVENT_OUTPUT),
|
|
3713
|
+
registration("Builtin", "Plain-JavaScript symbols available to a dynamic Client half.", "listBuiltins", () => ({
|
|
3714
|
+
builtins: [...CLIENT_BUILTIN_INSPECTION],
|
|
3715
|
+
referencedTypes: []
|
|
3716
|
+
})),
|
|
3717
|
+
{
|
|
3718
|
+
manifest: {
|
|
3719
|
+
id: "Slots",
|
|
3720
|
+
description: "Progressive live Slot inspection: compact purpose/topology trees plus one exact Slot contract.",
|
|
3721
|
+
methods: [{
|
|
3722
|
+
name: "listSubTree",
|
|
3723
|
+
description: "Return compact live Slot trees for navigation. With root, also return the selected Slot's full contract and occupants.",
|
|
3724
|
+
inputSchema: SUBTREE_INPUT,
|
|
3725
|
+
outputSchema: SUBTREE_OUTPUT
|
|
3726
|
+
}]
|
|
3727
|
+
},
|
|
3728
|
+
query(method, input) {
|
|
3729
|
+
if (method !== "listSubTree") throw new Error(`unknown Slots inspect method "${method}"`);
|
|
3730
|
+
const slots = ctx.get("slots");
|
|
3731
|
+
if (slots === void 0) throw new Error("Client Slots service is not running");
|
|
3732
|
+
const root = typeof input === "object" && input !== null && !Array.isArray(input) && typeof input.root === "string" ? input.root : void 0;
|
|
3733
|
+
const trees = slots.snapshot(root);
|
|
3734
|
+
const selected = trees[0];
|
|
3735
|
+
return Promise.resolve({
|
|
3736
|
+
...root === void 0 ? {} : { requestedRoot: {
|
|
3737
|
+
name: root,
|
|
3738
|
+
available: trees.length > 0
|
|
3739
|
+
} },
|
|
3740
|
+
trees: trees.map(compactSlotTree),
|
|
3741
|
+
...root === void 0 || selected === void 0 ? {} : { selected: inspectLiveSlot(selected) },
|
|
3742
|
+
referencedTypes: []
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
},
|
|
3746
|
+
registration("Theme", "Current theme token names and light/dark override requirements.", "listTokens", () => {
|
|
3747
|
+
const theme = ctx.get("theme");
|
|
3748
|
+
if (theme === void 0) throw new Error("Client Theme service is not running");
|
|
3749
|
+
return {
|
|
3750
|
+
tokens: theme.exportInspectTokens(),
|
|
3751
|
+
referencedTypes: []
|
|
3752
|
+
};
|
|
3753
|
+
})
|
|
3754
|
+
];
|
|
3755
|
+
}
|
|
3756
|
+
function registration(id, description, method, query, inputSchema = EMPTY_INPUT, outputSchema = ANY_OUTPUT) {
|
|
3757
|
+
return {
|
|
3758
|
+
manifest: {
|
|
3759
|
+
id,
|
|
3760
|
+
description,
|
|
3761
|
+
methods: [{
|
|
3762
|
+
name: method,
|
|
3763
|
+
description,
|
|
3764
|
+
inputSchema,
|
|
3765
|
+
outputSchema
|
|
3766
|
+
}]
|
|
3767
|
+
},
|
|
3768
|
+
async query(requested, input) {
|
|
3769
|
+
if (requested !== method) throw new Error(`unknown ${id} inspect method "${requested}"`);
|
|
3770
|
+
return await query(input);
|
|
3771
|
+
}
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
function exactInput(field, description) {
|
|
3775
|
+
return {
|
|
3776
|
+
type: "object",
|
|
3777
|
+
properties: { [field]: {
|
|
3778
|
+
type: "string",
|
|
3779
|
+
description
|
|
3780
|
+
} },
|
|
3781
|
+
additionalProperties: false
|
|
3782
|
+
};
|
|
3783
|
+
}
|
|
3784
|
+
function readExact(input, field) {
|
|
3785
|
+
if (input === void 0 || input === null || Array.isArray(input) || typeof input !== "object") return void 0;
|
|
3786
|
+
const value = input[field];
|
|
3787
|
+
return typeof value === "string" ? value : void 0;
|
|
3788
|
+
}
|
|
3789
|
+
const SLOT_CATALOG = new Map(CLIENT_SLOT_API.map((entry) => [entry.key, entry]));
|
|
3790
|
+
const GUARDED_SLOT_KEYS = new Map([["tool.view.cordis", {
|
|
3791
|
+
description: "fixed by the dynamic Client Guard",
|
|
3792
|
+
values: [{
|
|
3793
|
+
value: "self",
|
|
3794
|
+
description: "The only accepted key. The Guard binds it to this Package's pluginId and packageId."
|
|
3795
|
+
}]
|
|
3796
|
+
}]]);
|
|
3797
|
+
function compactSlotTree(node) {
|
|
3798
|
+
const catalog = SLOT_CATALOG.get(node.name);
|
|
3799
|
+
const guardedKeys = catalog === void 0 ? void 0 : GUARDED_SLOT_KEYS.get(catalog.key);
|
|
3800
|
+
return {
|
|
3801
|
+
name: node.name,
|
|
3802
|
+
kind: node.kind,
|
|
3803
|
+
scope: node.scope,
|
|
3804
|
+
...catalog === void 0 ? {} : {
|
|
3805
|
+
purpose: catalog.summary,
|
|
3806
|
+
replaceRisk: catalog.replaceRisk,
|
|
3807
|
+
...catalog.registerOptions.length === 0 ? {} : { registration: catalog.registerOptions.map((option) => ({
|
|
3808
|
+
name: option.name,
|
|
3809
|
+
type: option.type,
|
|
3810
|
+
required: option.requirement === "required"
|
|
3811
|
+
})) },
|
|
3812
|
+
...catalog.keyDomain === "" ? {} : {
|
|
3813
|
+
keyDomain: guardedKeys?.description ?? catalog.keyDomain,
|
|
3814
|
+
...guardedKeys === void 0 ? {} : { allowedKeys: guardedKeys.values.map((value) => ({ ...value })) }
|
|
3815
|
+
}
|
|
3816
|
+
},
|
|
3817
|
+
children: node.children.map(compactSlotTree)
|
|
3818
|
+
};
|
|
3819
|
+
}
|
|
3820
|
+
function inspectLiveSlot(node) {
|
|
3821
|
+
const catalog = SLOT_CATALOG.get(node.name);
|
|
3822
|
+
return {
|
|
3823
|
+
name: node.name,
|
|
3824
|
+
kind: node.kind,
|
|
3825
|
+
scope: node.scope,
|
|
3826
|
+
...node.declaredBy === void 0 ? {} : { declaredBy: node.declaredBy },
|
|
3827
|
+
occupants: node.occupants.map((occupant) => ({ ...occupant })),
|
|
3828
|
+
...catalog === void 0 ? {} : { catalog: inspectSlotCatalog(catalog) }
|
|
3829
|
+
};
|
|
3830
|
+
}
|
|
3831
|
+
function inspectSlotCatalog(entry) {
|
|
3832
|
+
const guardedKeys = GUARDED_SLOT_KEYS.get(entry.key);
|
|
3833
|
+
return {
|
|
3834
|
+
description: entry.doc,
|
|
3835
|
+
registration: entry.registerOptions.map((option) => ({
|
|
3836
|
+
name: option.name,
|
|
3837
|
+
type: option.type,
|
|
3838
|
+
required: option.requirement === "required",
|
|
3839
|
+
description: option.doc
|
|
3840
|
+
})),
|
|
3841
|
+
ownerProps: [...entry.ownerProps],
|
|
3842
|
+
ownerPropsReferences: [...entry.ownerPropsReferences],
|
|
3843
|
+
standardProps: [...entry.standardProps],
|
|
3844
|
+
keyDomain: guardedKeys?.description ?? entry.keyDomain,
|
|
3845
|
+
...guardedKeys === void 0 ? {} : { allowedKeys: guardedKeys.values.map((value) => ({ ...value })) },
|
|
3846
|
+
hookContext: entry.hookContext,
|
|
3847
|
+
slotInject: entry.slotInject,
|
|
3848
|
+
replaceRisk: entry.replaceRisk
|
|
3849
|
+
};
|
|
3850
|
+
}
|
|
3851
|
+
//#endregion
|
|
3852
|
+
//#region lib/types/client/timer.js
|
|
3853
|
+
/** Browser implementation of the Cordis timer Service. */
|
|
3854
|
+
/** Browser timer Service with the same public API as the Host Cordis TimerService. */
|
|
3855
|
+
var ClientTimerService = class extends _deepseek_ai_cordis.Service {
|
|
3856
|
+
/** Register the Service and mix its lifecycle-safe helpers onto Context. */
|
|
3857
|
+
constructor(ctx) {
|
|
3858
|
+
super(ctx, "timer");
|
|
3859
|
+
ctx.mixin("timer", [
|
|
3860
|
+
"timeout",
|
|
3861
|
+
"interval",
|
|
3862
|
+
"throttle",
|
|
3863
|
+
"debounce",
|
|
3864
|
+
"setTimeout",
|
|
3865
|
+
"setInterval"
|
|
3866
|
+
]);
|
|
3867
|
+
}
|
|
3868
|
+
/**
|
|
3869
|
+
* Run a callback once through {@link timeout}.
|
|
3870
|
+
* @param callback - Work to run after the delay.
|
|
3871
|
+
* @param delay - Delay in milliseconds.
|
|
3872
|
+
* @returns Disposer that cancels the pending callback early.
|
|
3873
|
+
* @deprecated Use `ctx.timeout()` instead.
|
|
3874
|
+
*/
|
|
3875
|
+
setTimeout(callback, delay) {
|
|
3876
|
+
return this.timeout(callback, delay);
|
|
3877
|
+
}
|
|
3878
|
+
/**
|
|
3879
|
+
* Run a callback repeatedly through {@link interval}.
|
|
3880
|
+
* @param callback - Work to run on each tick.
|
|
3881
|
+
* @param delay - Interval in milliseconds.
|
|
3882
|
+
* @returns Disposer that stops the interval early.
|
|
3883
|
+
* @deprecated Use `ctx.interval()` instead.
|
|
3884
|
+
*/
|
|
3885
|
+
setInterval(callback, delay) {
|
|
3886
|
+
return this.interval(callback, delay);
|
|
3887
|
+
}
|
|
3888
|
+
timeout(...args) {
|
|
3889
|
+
const callback = typeof args[0] === "function" ? args.shift() : void 0;
|
|
3890
|
+
const delay = args[0];
|
|
3891
|
+
if (callback !== void 0) {
|
|
3892
|
+
const dispose = this.ctx.effect(() => {
|
|
3893
|
+
const timer = globalThis.setTimeout(() => {
|
|
3894
|
+
dispose();
|
|
3895
|
+
callback();
|
|
3896
|
+
}, delay);
|
|
3897
|
+
return () => {
|
|
3898
|
+
globalThis.clearTimeout(timer);
|
|
3899
|
+
};
|
|
3900
|
+
}, "ctx.timeout()");
|
|
3901
|
+
return dispose;
|
|
3902
|
+
}
|
|
3903
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
3904
|
+
const dispose = this.ctx.effect(() => {
|
|
3905
|
+
const timer = globalThis.setTimeout(resolve, delay);
|
|
3906
|
+
return () => {
|
|
3907
|
+
globalThis.clearTimeout(timer);
|
|
3908
|
+
reject(/* @__PURE__ */ new Error("Context has been disposed"));
|
|
3909
|
+
};
|
|
3910
|
+
}, "ctx.timeout()");
|
|
3911
|
+
return promise.finally(() => {
|
|
3912
|
+
dispose();
|
|
3913
|
+
});
|
|
3914
|
+
}
|
|
3915
|
+
interval(...args) {
|
|
3916
|
+
const callback = typeof args[0] === "function" ? args.shift() : void 0;
|
|
3917
|
+
const delay = args[0];
|
|
3918
|
+
if (callback !== void 0) return this.ctx.effect(() => {
|
|
3919
|
+
const timer = globalThis.setInterval(callback, delay);
|
|
3920
|
+
return () => {
|
|
3921
|
+
globalThis.clearInterval(timer);
|
|
3922
|
+
};
|
|
3923
|
+
}, "ctx.interval()");
|
|
3924
|
+
let done;
|
|
3925
|
+
let nextTask;
|
|
3926
|
+
const dispose = this.ctx.effect(() => {
|
|
3927
|
+
const timer = globalThis.setInterval(() => {
|
|
3928
|
+
nextTask?.resolve({
|
|
3929
|
+
done: false,
|
|
3930
|
+
value: void 0
|
|
3931
|
+
});
|
|
3932
|
+
}, delay);
|
|
3933
|
+
return () => {
|
|
3934
|
+
globalThis.clearInterval(timer);
|
|
3935
|
+
if (done !== void 0) return;
|
|
3936
|
+
done = {
|
|
3937
|
+
kind: "throw",
|
|
3938
|
+
reason: /* @__PURE__ */ new Error("Context has been disposed")
|
|
3939
|
+
};
|
|
3940
|
+
nextTask?.reject(done.reason);
|
|
3941
|
+
};
|
|
3942
|
+
}, "ctx.interval()");
|
|
3943
|
+
return {
|
|
3944
|
+
next: () => {
|
|
3945
|
+
if (done === void 0) return (nextTask = Promise.withResolvers()).promise;
|
|
3946
|
+
if (done.kind === "return") return Promise.resolve({
|
|
3947
|
+
done: true,
|
|
3948
|
+
value: done.value
|
|
3949
|
+
});
|
|
3950
|
+
return Promise.reject(done.reason);
|
|
3951
|
+
},
|
|
3952
|
+
return: (value) => {
|
|
3953
|
+
if (done === void 0) done = {
|
|
3954
|
+
kind: "return",
|
|
3955
|
+
value
|
|
3956
|
+
};
|
|
3957
|
+
nextTask?.resolve({
|
|
3958
|
+
done: true,
|
|
3959
|
+
value
|
|
3960
|
+
});
|
|
3961
|
+
dispose();
|
|
3962
|
+
return Promise.resolve({
|
|
3963
|
+
done: true,
|
|
3964
|
+
value
|
|
3965
|
+
});
|
|
3966
|
+
},
|
|
3967
|
+
throw: (reason) => {
|
|
3968
|
+
if (done === void 0) done = {
|
|
3969
|
+
kind: "throw",
|
|
3970
|
+
reason
|
|
3971
|
+
};
|
|
3972
|
+
nextTask?.reject(reason);
|
|
3973
|
+
dispose();
|
|
3974
|
+
return Promise.resolve({
|
|
3975
|
+
done: true,
|
|
3976
|
+
value: void 0
|
|
3977
|
+
});
|
|
3978
|
+
},
|
|
3979
|
+
[Symbol.asyncIterator]() {
|
|
3980
|
+
return this;
|
|
3981
|
+
}
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
/** Build a delayed wrapper whose pending callback belongs to the calling Fiber. */
|
|
3985
|
+
schedule(label, trigger, disposed = false) {
|
|
3986
|
+
let timer;
|
|
3987
|
+
const dispose = this.ctx.effect(() => () => {
|
|
3988
|
+
disposed = true;
|
|
3989
|
+
globalThis.clearTimeout(timer);
|
|
3990
|
+
}, label);
|
|
3991
|
+
const wrapper = (...args) => {
|
|
3992
|
+
globalThis.clearTimeout(timer);
|
|
3993
|
+
timer = trigger(args, disposed);
|
|
3994
|
+
};
|
|
3995
|
+
wrapper.dispose = dispose;
|
|
3996
|
+
return wrapper;
|
|
3997
|
+
}
|
|
3998
|
+
/**
|
|
3999
|
+
* Return a throttled function whose timer is disposed with the calling Fiber.
|
|
4000
|
+
* @param callback - Function to throttle.
|
|
4001
|
+
* @param delay - Minimum interval between calls in milliseconds.
|
|
4002
|
+
* @param noTrailing - Whether to suppress a delayed trailing call.
|
|
4003
|
+
* @returns Throttled function with an early disposer.
|
|
4004
|
+
*/
|
|
4005
|
+
throttle(callback, delay, noTrailing) {
|
|
4006
|
+
let lastCall = -Infinity;
|
|
4007
|
+
const execute = (...args) => {
|
|
4008
|
+
lastCall = Date.now();
|
|
4009
|
+
callback(...args);
|
|
4010
|
+
};
|
|
4011
|
+
return this.schedule("ctx.throttle()", (args, disposed) => {
|
|
4012
|
+
const remaining = delay - Date.now() + lastCall;
|
|
4013
|
+
if (remaining <= 0) execute(...args);
|
|
4014
|
+
else if (!disposed) return globalThis.setTimeout(execute, remaining, ...args);
|
|
4015
|
+
}, noTrailing);
|
|
4016
|
+
}
|
|
4017
|
+
/**
|
|
4018
|
+
* Return a debounced function whose timer is disposed with the calling Fiber.
|
|
4019
|
+
* @param callback - Function to debounce.
|
|
4020
|
+
* @param delay - Quiet period in milliseconds.
|
|
4021
|
+
* @returns Debounced function with an early disposer.
|
|
4022
|
+
*/
|
|
4023
|
+
debounce(callback, delay) {
|
|
4024
|
+
return this.schedule("ctx.debounce()", (args, disposed) => {
|
|
4025
|
+
if (disposed) return;
|
|
4026
|
+
return globalThis.setTimeout(callback, delay, ...args);
|
|
4027
|
+
});
|
|
4028
|
+
}
|
|
4029
|
+
};
|
|
4030
|
+
/**
|
|
4031
|
+
* Install the browser timer Service on one Client composition.
|
|
4032
|
+
* @param ctx - Client context that owns the Service and mixed-in helpers.
|
|
4033
|
+
* @returns Nothing after registering the Service.
|
|
4034
|
+
*/
|
|
4035
|
+
function provideClientTimer(ctx) {
|
|
4036
|
+
new ClientTimerService(ctx);
|
|
4037
|
+
}
|
|
4038
|
+
//#endregion
|
|
4039
|
+
//#region lib/types/client/index.js
|
|
4040
|
+
/**
|
|
4041
|
+
* Dynamic-package runner, browser half: the load engine that turns one browser
|
|
4042
|
+
* half's source into a live cordis plugin (closure → guard → module table →
|
|
4043
|
+
* loader entry, ./runtime.ts), plus the retract announcement that unloads it.
|
|
4044
|
+
*
|
|
4045
|
+
* Nothing loads on activation: this page holds no dynamic package until a
|
|
4046
|
+
* dispatch arrives, and a dispatch only follows a model `cordis_run` or a user
|
|
4047
|
+
* pressing a card's start control. A refresh therefore starts clean by design —
|
|
4048
|
+
* host process memory still holds the definition, the page simply does not run
|
|
4049
|
+
* it until asked again.
|
|
4050
|
+
*/
|
|
4051
|
+
/** Teaching text for a routing failure the infrastructure itself reports. */
|
|
4052
|
+
function invokeFailure(pluginId, method, result) {
|
|
4053
|
+
const where = `host.call("${method}") on ${pluginId}`;
|
|
4054
|
+
if (result.code === "plugin-not-running") return `${where} found no active Host half — the Plugin is stopped or was removed.`;
|
|
4055
|
+
if (result.code === "stale-run") return `${where} belongs to an activation that has already been replaced.`;
|
|
4056
|
+
if (result.code === "method-not-found") return `${where} is not registered: the host half must declare it with harness.handle("${method}", fn).`;
|
|
4057
|
+
return `${where} failed inside the host handler: ${result.message}`;
|
|
4058
|
+
}
|
|
4059
|
+
/** Preserve a Host handler's stack while adding the Client call site diagnosis. */
|
|
4060
|
+
function invokeError(pluginId, method, result) {
|
|
4061
|
+
const error = new Error(invokeFailure(pluginId, method, result));
|
|
4062
|
+
if (result.stack !== void 0) error.stack = `${error.stack ?? error.message}\nHost stack:\n${result.stack}`;
|
|
4063
|
+
return error;
|
|
4064
|
+
}
|
|
4065
|
+
/**
|
|
4066
|
+
* Teaching text for a `host.call` the wire itself refused: the generated codec
|
|
4067
|
+
* rejected the argument before sending, or the result on the way back, or the
|
|
4068
|
+
* transport broke. The infrastructure's message names the field it refused but
|
|
4069
|
+
* not the call it belonged to, and the model authored both halves — so this adds
|
|
4070
|
+
* the call and the contract it has to satisfy.
|
|
4071
|
+
*/
|
|
4072
|
+
function wireFailure(id, method, error) {
|
|
4073
|
+
return `host.call("${method}") on ${id} did not complete: ${error instanceof Error ? error.message : String(error)}\nBoth directions carry JSON only: pass plain JSON data as the argument — or omit it, and the handler receives null — and answer from harness.handle("${method}", fn) with JSON (\`return null\` when there is nothing to report).`;
|
|
4074
|
+
}
|
|
4075
|
+
/** Stable Cordis plugin name. */
|
|
4076
|
+
const name = "cordis-client-runner";
|
|
4077
|
+
/**
|
|
4078
|
+
* Required services: the loader/module chain for entries, the slot registry for
|
|
4079
|
+
* contributions, and the `dynamicCordisRunner` Remote namespace. Declaring the
|
|
4080
|
+
* namespace parks this plugin until the host side exists, so a page never loads
|
|
4081
|
+
* a browser half whose host half it could not reach.
|
|
4082
|
+
*/
|
|
4083
|
+
const inject = [
|
|
4084
|
+
"loader",
|
|
4085
|
+
"modules",
|
|
4086
|
+
"slots",
|
|
4087
|
+
"remote",
|
|
4088
|
+
"remote.dynamicCordisRunner"
|
|
4089
|
+
];
|
|
4090
|
+
/**
|
|
4091
|
+
* Client plugin body: build the runner and subscribe the dispatch family.
|
|
4092
|
+
* @param ctx - client root context.
|
|
4093
|
+
*/
|
|
4094
|
+
function apply(ctx) {
|
|
4095
|
+
provideClientTimer(ctx);
|
|
4096
|
+
const inspect = new ClientCordisInspectRegistry({
|
|
4097
|
+
sync: async (providers) => {
|
|
4098
|
+
const answered = await ctx.remote.dynamicCordisRunner.syncInspectManifest(providers);
|
|
4099
|
+
if (!answered.ok) throw new Error(`${answered.error.code}: ${answered.error.message}`);
|
|
4100
|
+
},
|
|
4101
|
+
resolve: async (agentId, requestId, resolution) => {
|
|
4102
|
+
const answered = await ctx.remote.dynamicCordisRunner.resolveInspectQuery(agentId, requestId, resolution);
|
|
4103
|
+
if (!answered.ok) throw new Error(`${answered.error.code}: ${answered.error.message}`);
|
|
4104
|
+
}
|
|
4105
|
+
});
|
|
4106
|
+
provideClientCordisInspect(ctx, inspect);
|
|
4107
|
+
for (const provider of clientInspectProviders(ctx)) ctx.effect(() => inspect.register(provider), `cordis-client-runner: inspect ${provider.manifest.id}`);
|
|
4108
|
+
ctx.on("connection/reset", () => {
|
|
4109
|
+
inspect.publish();
|
|
4110
|
+
});
|
|
4111
|
+
const runner = new DynamicCordisPackageRunner({
|
|
4112
|
+
ctx,
|
|
4113
|
+
loader: ctx.loader,
|
|
4114
|
+
modules: ctx.get("modules"),
|
|
4115
|
+
slots: ctx.get("slots"),
|
|
4116
|
+
invoke: async (pluginId, pluginRunId, method, args) => {
|
|
4117
|
+
const answered = await ctx.remote.dynamicCordisRunner.invoke(pluginId, pluginRunId, method, args).catch((error) => {
|
|
4118
|
+
throw new Error(wireFailure(pluginId, method, error));
|
|
4119
|
+
});
|
|
4120
|
+
if (!answered.ok) throw new Error(wireFailure(pluginId, method, `${answered.error.code}: ${answered.error.message}`));
|
|
4121
|
+
const result = answered.value;
|
|
4122
|
+
if (result.ok) return result.value;
|
|
4123
|
+
throw invokeError(pluginId, method, result);
|
|
4124
|
+
},
|
|
4125
|
+
reportRenderFailure: (agentId, pluginId, pluginRunId, failure) => {
|
|
4126
|
+
ctx.remote.dynamicCordisRunner.reportRenderFailure(agentId, pluginId, pluginRunId, failure).then((result) => {
|
|
4127
|
+
if (!result.ok) console.error(`[cordis-client-runner] reporting a render failure of ${pluginId} failed:`, result.error);
|
|
4128
|
+
}, (error) => {
|
|
4129
|
+
console.error(`[cordis-client-runner] reporting a render failure of ${pluginId} failed:`, error);
|
|
4130
|
+
});
|
|
4131
|
+
},
|
|
4132
|
+
reportGuardFailure: (agentId, pluginId, pluginRunId, failure) => {
|
|
4133
|
+
ctx.remote.dynamicCordisRunner.reportClientGuardFailure(agentId, pluginId, pluginRunId, failure).then((result) => {
|
|
4134
|
+
if (!result.ok) console.error(`[cordis-client-runner] reporting a guard failure of ${pluginId} failed:`, result.error);
|
|
4135
|
+
}, (error) => {
|
|
4136
|
+
console.error(`[cordis-client-runner] reporting a guard failure of ${pluginId} failed:`, error);
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
4139
|
+
});
|
|
4140
|
+
const orchestrator = new CordisRunOrchestrator({
|
|
4141
|
+
runner,
|
|
4142
|
+
host: {
|
|
4143
|
+
runHostHalf: async (agentId, pluginId, packageId, mode, requestId, approveFutureVersions) => {
|
|
4144
|
+
const answered = await ctx.remote.dynamicCordisRunner.runHostHalf(agentId, pluginId, packageId, mode, requestId, approveFutureVersions);
|
|
4145
|
+
return answered.ok ? answered.value : {
|
|
4146
|
+
ok: false,
|
|
4147
|
+
message: `${answered.error.code}: ${answered.error.message}`
|
|
4148
|
+
};
|
|
4149
|
+
},
|
|
4150
|
+
getClientCode: async (agentId, pluginId, pluginRunId) => {
|
|
4151
|
+
const answered = await ctx.remote.dynamicCordisRunner.getClientCode(agentId, pluginId, pluginRunId);
|
|
4152
|
+
if (!answered.ok) throw new Error(`${answered.error.code}: ${answered.error.message}`);
|
|
4153
|
+
return answered.value;
|
|
4154
|
+
},
|
|
4155
|
+
resolveRequestRun: async (requestId, resolution) => {
|
|
4156
|
+
const answered = await ctx.remote.dynamicCordisRunner.resolveRequestRun(requestId, resolution);
|
|
4157
|
+
if (!answered.ok) throw new Error(`${answered.error.code}: ${answered.error.message}`);
|
|
4158
|
+
return answered.value;
|
|
4159
|
+
},
|
|
4160
|
+
settleUserRun: async (agentId, pluginId, resolution) => {
|
|
4161
|
+
const answered = await ctx.remote.dynamicCordisRunner.settleUserRun(agentId, pluginId, resolution);
|
|
4162
|
+
if (!answered.ok) throw new Error(`${answered.error.code}: ${answered.error.message}`);
|
|
4163
|
+
return answered.value;
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
});
|
|
4167
|
+
const face = {
|
|
4168
|
+
activeRuns: orchestrator.activeRuns,
|
|
4169
|
+
lastRunError: orchestrator.lastRunError,
|
|
4170
|
+
renderFailures: runner.renderFailures,
|
|
4171
|
+
reconcileApprovals: (rows) => {
|
|
4172
|
+
orchestrator.reconcileApprovals(rows);
|
|
4173
|
+
},
|
|
4174
|
+
approve: (requestId, approveFutureVersions) => orchestrator.approve(requestId, approveFutureVersions),
|
|
4175
|
+
decline: (requestId) => orchestrator.decline(requestId),
|
|
4176
|
+
startUserRun: (request) => orchestrator.startUserRun(request),
|
|
4177
|
+
subscribe: (fn) => runner.subscribe(fn),
|
|
4178
|
+
getSnapshot: () => runner.getSnapshot(),
|
|
4179
|
+
isLoaded: (id) => runner.isLoaded(id)
|
|
4180
|
+
};
|
|
4181
|
+
ctx.provide("dynamicCordisRunner", face);
|
|
4182
|
+
ctx.effect(() => () => {
|
|
4183
|
+
runner.dispose();
|
|
4184
|
+
}, "cordis-client-runner: dynamic package runner");
|
|
4185
|
+
ctx.remote.$on("cordis/request-run", (request) => {
|
|
4186
|
+
orchestrator.open(request);
|
|
4187
|
+
});
|
|
4188
|
+
ctx.remote.$on("cordis/request-run-resolved", (resolved) => {
|
|
4189
|
+
orchestrator.close(resolved.requestId);
|
|
4190
|
+
});
|
|
4191
|
+
ctx.remote.$on("cordis/dynamic-retract", (retracted) => {
|
|
4192
|
+
runner.retract(retracted.pluginId, retracted.pluginRunId);
|
|
4193
|
+
});
|
|
4194
|
+
ctx.remote.$on("cordis/inspect-query", (request) => {
|
|
4195
|
+
inspect.query(request).catch((error) => {
|
|
4196
|
+
console.error(`[cordis-client-runner] inspect query ${request.provider}.${request.method} failed:`, error);
|
|
4197
|
+
});
|
|
4198
|
+
});
|
|
4199
|
+
ctx.remote.$on("cordis/inspect-query-resolved", (resolved) => {
|
|
4200
|
+
inspect.close(resolved.requestId);
|
|
4201
|
+
});
|
|
4202
|
+
}
|
|
4203
|
+
//#endregion
|
|
4204
|
+
exports.ClientCordisInspectRegistry = ClientCordisInspectRegistry;
|
|
4205
|
+
exports.ClientTimerService = ClientTimerService;
|
|
4206
|
+
exports.CordisRunOrchestrator = CordisRunOrchestrator;
|
|
4207
|
+
exports.DynamicCordisPackageRunner = DynamicCordisPackageRunner;
|
|
4208
|
+
exports.DynamicCordisStyles = DynamicCordisStyles;
|
|
4209
|
+
exports.apply = apply;
|
|
4210
|
+
exports.dynamicCordisContext = dynamicCordisContext;
|
|
4211
|
+
exports.evaluateClientHalf = evaluateClientHalf;
|
|
4212
|
+
exports.inject = inject;
|
|
4213
|
+
exports.isDynamicCordisPlugin = isDynamicCordisPlugin;
|
|
4214
|
+
exports.name = name;
|
|
4215
|
+
return module.exports;
|
|
4216
|
+
}
|
|
4217
|
+
});
|
|
4218
|
+
|
|
4219
|
+
//# sourceMappingURL=client.js.map
|