@yaklabs-ai/plugin-sdk 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD-PARTY-INVENTORY.json +1 -1
- package/dist/disclosure.d.ts +34 -0
- package/dist/jev.d.ts +76 -0
- package/dist/jev.js +63 -0
- package/dist/kay-agent-testing.d.ts +34 -0
- package/dist/kay-agent.d.ts +34 -0
- package/dist/kay-app.d.ts +99 -25
- package/dist/kay-app.js +11 -0
- package/dist/signals.d.ts +365 -0
- package/dist/signals.js +132 -0
- package/package.json +9 -1
package/README.md
CHANGED
package/dist/disclosure.d.ts
CHANGED
|
@@ -2700,6 +2700,24 @@ declare interface UriModule {
|
|
|
2700
2700
|
readonly authActions?: UriAuthActionPolicy;
|
|
2701
2701
|
/** Pure route/payload validation applied before a consequential permission ask. */
|
|
2702
2702
|
readonly preflight?: UriOperationPreflightPolicy;
|
|
2703
|
+
/**
|
|
2704
|
+
* Structured facts naming the WHOLE consequence of this call, or `undefined` when
|
|
2705
|
+
* the route cannot honestly be described on a one-line approval surface. The module
|
|
2706
|
+
* owns the static action phrase, target resolution, payload decoding and declaration
|
|
2707
|
+
* of covered payload fields and query parameters. Unknown fields cost the summary.
|
|
2708
|
+
* The host owns framing, the whole-quote fit (no elision) and display-safety admission.
|
|
2709
|
+
*
|
|
2710
|
+
* Unlike preflight, this hook is async and may do I/O. Resolve names through `ctx`,
|
|
2711
|
+
* the same dispatch context used by execution, never an ambient or cached account.
|
|
2712
|
+
* The host calls it once before raising the ask, enforces the short `ctx.signal`
|
|
2713
|
+
* deadline and discards late answers. A throw yields no summary and a module-attributed
|
|
2714
|
+
* diagnostic; declining costs a click into the app, never an under-described approval.
|
|
2715
|
+
*/
|
|
2716
|
+
describeOperation?(request: {
|
|
2717
|
+
readonly operation: UriOperation;
|
|
2718
|
+
readonly uri: string;
|
|
2719
|
+
readonly payload: unknown;
|
|
2720
|
+
}, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
|
|
2703
2721
|
/**
|
|
2704
2722
|
* Per-resource observability: does a `format: "observation"` read of THIS
|
|
2705
2723
|
* uri render source-native material ({@link UriResolveContext.format})?
|
|
@@ -2813,6 +2831,22 @@ declare interface UriNormalizeResult {
|
|
|
2813
2831
|
|
|
2814
2832
|
declare type UriOperation = "help" | "read" | "write" | "edit" | "list" | "search" | "delete";
|
|
2815
2833
|
|
|
2834
|
+
declare interface UriOperationDescription {
|
|
2835
|
+
/** Static verb phrase authored by the module. Never interpolated from input. */
|
|
2836
|
+
readonly action: string;
|
|
2837
|
+
/** Human name resolved through the connection that will execute the call. */
|
|
2838
|
+
readonly target: string;
|
|
2839
|
+
/** Model-authored content; the host flattens, budgets, elides and bounds it. */
|
|
2840
|
+
readonly quote?: string;
|
|
2841
|
+
/** What this description accounts for; the host refuses undeclared input. */
|
|
2842
|
+
readonly accounts: {
|
|
2843
|
+
/** The payload actually read by the module, after its own decoding. */
|
|
2844
|
+
readonly payload: unknown;
|
|
2845
|
+
readonly payloadFields: readonly string[];
|
|
2846
|
+
readonly queryParams?: readonly string[];
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2816
2850
|
/**
|
|
2817
2851
|
* Pure, route-aware validation that runs before permission policy can surface an ask.
|
|
2818
2852
|
*
|
package/dist/jev.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Questions } from '@typesafe-ai/sdk';
|
|
2
|
+
import { SystemOneRequest } from '@typesafe-ai/sdk';
|
|
3
|
+
import { SystemOneResult } from '@typesafe-ai/sdk';
|
|
4
|
+
import { TypeSafeClient } from '@typesafe-ai/sdk';
|
|
5
|
+
import { Usage } from '@typesafe-ai/sdk';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The `withJev` a host with no Jev lane binds: `use` is never called, no client is ever built,
|
|
9
|
+
* and no network is ever touched. It keeps the cancellation contract identical to the real
|
|
10
|
+
* wrapper's so a caller's `signal`/`deadlineAt` handling does not change with host composition.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately here rather than in the runtime: this is the DEFAULT for the SDK's own host
|
|
13
|
+
* factory, which cannot import the runtime uphill. The runtime also delegates fallback execution
|
|
14
|
+
* here, so both paths share cancellation, deadline enforcement, and cleanup.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createFallbackOnlyWithJev(): WithJev;
|
|
17
|
+
|
|
18
|
+
export { Questions }
|
|
19
|
+
|
|
20
|
+
export { SystemOneRequest }
|
|
21
|
+
|
|
22
|
+
export { SystemOneResult }
|
|
23
|
+
|
|
24
|
+
export { TypeSafeClient }
|
|
25
|
+
|
|
26
|
+
export { Usage }
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run `use` against a live Jev client, falling back to `fallback` whenever Jev cannot serve the
|
|
30
|
+
* call. The wrapper exists so a feature can ASK for a typed decision without ever depending on
|
|
31
|
+
* one: `fallback` is required, and it runs whenever no host-managed key is saved,
|
|
32
|
+
* the user has not granted `jev.use`, the branch exceeds its budget, `use` throws, or
|
|
33
|
+
* `use` declines by returning `undefined`. With valid options, this rejects on an explicit
|
|
34
|
+
* `signal` abort, reaching `deadlineAt`, or `fallback` itself throwing.
|
|
35
|
+
*
|
|
36
|
+
* ALWAYS PRESENT on the plugin API, on every host. A host that composed no Jev lane binds
|
|
37
|
+
* {@link createFallbackOnlyWithJev}, so a caller never branches on availability — branching is
|
|
38
|
+
* exactly how a fallback gets skipped.
|
|
39
|
+
*
|
|
40
|
+
* `jev` is the vendor's own `TypeSafeClient`, configured with the host's default model and base
|
|
41
|
+
* URL. Native `request.model` overrides remain supported. It
|
|
42
|
+
* is valid only for the duration of the callback: a reference retained past `use`'s scope is
|
|
43
|
+
* refused at the transport, and an in-flight request is aborted when the call stops waiting.
|
|
44
|
+
* The callback itself is ABANDONED, not interrupted — a host cannot stop arbitrary JavaScript
|
|
45
|
+
* that is already running, so `use` must not perform side effects it would mind completing
|
|
46
|
+
* unobserved. The same applies to a running fallback: abort/deadline stops waiting for it but
|
|
47
|
+
* cannot interrupt its JavaScript.
|
|
48
|
+
*/
|
|
49
|
+
export declare type WithJev = <T>(
|
|
50
|
+
/** Return `undefined` to decline (the fallback runs). A feature whose real result can itself be
|
|
51
|
+
* `undefined` should return `null` or its own wrapper type instead. */
|
|
52
|
+
use: (jev: TypeSafeClient) => T | undefined | PromiseLike<T | undefined>,
|
|
53
|
+
/** Required. The behaviour the feature has without Jev. */
|
|
54
|
+
fallback: () => T | PromiseLike<T>, options?: WithJevOptions) => Promise<T>;
|
|
55
|
+
|
|
56
|
+
/** Per-call knobs for {@link WithJev}. Every field is optional; the host applies its own default
|
|
57
|
+
* budget when none is given, so the Jev branch is never unbounded. */
|
|
58
|
+
export declare interface WithJevOptions {
|
|
59
|
+
/**
|
|
60
|
+
* Explicit cancellation. Always propagates as a rejection; never substituted with `fallback`,
|
|
61
|
+
* because an aborted call means "stop", not "use the cheaper path".
|
|
62
|
+
*/
|
|
63
|
+
readonly signal?: AbortSignal;
|
|
64
|
+
/** Bounds the Jev branch alone, measured from the call. */
|
|
65
|
+
readonly timeoutMs?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Absolute epoch-ms hard deadline for the WHOLE call, `fallback` included. A deadline already
|
|
68
|
+
* passed rejects (like `signal`); one still ahead derives the Jev branch's budget from it and
|
|
69
|
+
* keeps a small reserve so `fallback` has a window to run.
|
|
70
|
+
*/
|
|
71
|
+
readonly deadlineAt?: number;
|
|
72
|
+
/** Attribution slug for host usage accounting. Never sent to Jev. */
|
|
73
|
+
readonly purpose?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { }
|
package/dist/jev.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// packages/plugin-sdk/dist/jev.js
|
|
2
|
+
function createFallbackOnlyWithJev() {
|
|
3
|
+
return async (_use, fallback, options = {}) => {
|
|
4
|
+
if (typeof fallback !== "function") {
|
|
5
|
+
throw new TypeError("withJev requires a `fallback` function as its second argument.");
|
|
6
|
+
}
|
|
7
|
+
const { signal, deadlineAt } = options;
|
|
8
|
+
if (deadlineAt !== void 0 && !Number.isFinite(deadlineAt)) {
|
|
9
|
+
throw new TypeError("withJev's `deadlineAt` must be a finite epoch-ms timestamp.");
|
|
10
|
+
}
|
|
11
|
+
const assertWithinBoundaries = () => {
|
|
12
|
+
if (signal?.aborted === true) {
|
|
13
|
+
throw signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
14
|
+
}
|
|
15
|
+
if (deadlineAt !== void 0 && Date.now() >= deadlineAt) {
|
|
16
|
+
throw new Error("withJev's deadline has passed.");
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
assertWithinBoundaries();
|
|
20
|
+
let timer;
|
|
21
|
+
let onAbort;
|
|
22
|
+
const stopped = new Promise((_resolve, reject) => {
|
|
23
|
+
if (signal !== void 0) {
|
|
24
|
+
onAbort = () => reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
25
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
26
|
+
}
|
|
27
|
+
if (deadlineAt !== void 0) {
|
|
28
|
+
const checkDeadline = () => {
|
|
29
|
+
const remaining = deadlineAt - Date.now();
|
|
30
|
+
if (remaining <= 0) {
|
|
31
|
+
reject(new Error("withJev's deadline has passed."));
|
|
32
|
+
} else {
|
|
33
|
+
timer = setTimeout(checkDeadline, Math.min(remaining, 2147483647));
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
checkDeadline();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
const result = await Promise.race([
|
|
41
|
+
Promise.resolve().then(() => {
|
|
42
|
+
assertWithinBoundaries();
|
|
43
|
+
return fallback();
|
|
44
|
+
}),
|
|
45
|
+
stopped
|
|
46
|
+
]);
|
|
47
|
+
assertWithinBoundaries();
|
|
48
|
+
return result;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
assertWithinBoundaries();
|
|
51
|
+
throw error;
|
|
52
|
+
} finally {
|
|
53
|
+
if (timer !== void 0)
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
if (signal !== void 0 && onAbort !== void 0) {
|
|
56
|
+
signal.removeEventListener("abort", onAbort);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
createFallbackOnlyWithJev
|
|
63
|
+
};
|
|
@@ -2563,6 +2563,24 @@ declare interface UriModule {
|
|
|
2563
2563
|
readonly authActions?: UriAuthActionPolicy;
|
|
2564
2564
|
/** Pure route/payload validation applied before a consequential permission ask. */
|
|
2565
2565
|
readonly preflight?: UriOperationPreflightPolicy;
|
|
2566
|
+
/**
|
|
2567
|
+
* Structured facts naming the WHOLE consequence of this call, or `undefined` when
|
|
2568
|
+
* the route cannot honestly be described on a one-line approval surface. The module
|
|
2569
|
+
* owns the static action phrase, target resolution, payload decoding and declaration
|
|
2570
|
+
* of covered payload fields and query parameters. Unknown fields cost the summary.
|
|
2571
|
+
* The host owns framing, the whole-quote fit (no elision) and display-safety admission.
|
|
2572
|
+
*
|
|
2573
|
+
* Unlike preflight, this hook is async and may do I/O. Resolve names through `ctx`,
|
|
2574
|
+
* the same dispatch context used by execution, never an ambient or cached account.
|
|
2575
|
+
* The host calls it once before raising the ask, enforces the short `ctx.signal`
|
|
2576
|
+
* deadline and discards late answers. A throw yields no summary and a module-attributed
|
|
2577
|
+
* diagnostic; declining costs a click into the app, never an under-described approval.
|
|
2578
|
+
*/
|
|
2579
|
+
describeOperation?(request: {
|
|
2580
|
+
readonly operation: UriOperation;
|
|
2581
|
+
readonly uri: string;
|
|
2582
|
+
readonly payload: unknown;
|
|
2583
|
+
}, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
|
|
2566
2584
|
/**
|
|
2567
2585
|
* Per-resource observability: does a `format: "observation"` read of THIS
|
|
2568
2586
|
* uri render source-native material ({@link UriResolveContext.format})?
|
|
@@ -2676,6 +2694,22 @@ declare interface UriNormalizeResult {
|
|
|
2676
2694
|
|
|
2677
2695
|
declare type UriOperation = "help" | "read" | "write" | "edit" | "list" | "search" | "delete";
|
|
2678
2696
|
|
|
2697
|
+
declare interface UriOperationDescription {
|
|
2698
|
+
/** Static verb phrase authored by the module. Never interpolated from input. */
|
|
2699
|
+
readonly action: string;
|
|
2700
|
+
/** Human name resolved through the connection that will execute the call. */
|
|
2701
|
+
readonly target: string;
|
|
2702
|
+
/** Model-authored content; the host flattens, budgets, elides and bounds it. */
|
|
2703
|
+
readonly quote?: string;
|
|
2704
|
+
/** What this description accounts for; the host refuses undeclared input. */
|
|
2705
|
+
readonly accounts: {
|
|
2706
|
+
/** The payload actually read by the module, after its own decoding. */
|
|
2707
|
+
readonly payload: unknown;
|
|
2708
|
+
readonly payloadFields: readonly string[];
|
|
2709
|
+
readonly queryParams?: readonly string[];
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2679
2713
|
/**
|
|
2680
2714
|
* Pure, route-aware validation that runs before permission policy can surface an ask.
|
|
2681
2715
|
*
|
package/dist/kay-agent.d.ts
CHANGED
|
@@ -4323,6 +4323,24 @@ export declare interface UriModule {
|
|
|
4323
4323
|
readonly authActions?: UriAuthActionPolicy;
|
|
4324
4324
|
/** Pure route/payload validation applied before a consequential permission ask. */
|
|
4325
4325
|
readonly preflight?: UriOperationPreflightPolicy;
|
|
4326
|
+
/**
|
|
4327
|
+
* Structured facts naming the WHOLE consequence of this call, or `undefined` when
|
|
4328
|
+
* the route cannot honestly be described on a one-line approval surface. The module
|
|
4329
|
+
* owns the static action phrase, target resolution, payload decoding and declaration
|
|
4330
|
+
* of covered payload fields and query parameters. Unknown fields cost the summary.
|
|
4331
|
+
* The host owns framing, the whole-quote fit (no elision) and display-safety admission.
|
|
4332
|
+
*
|
|
4333
|
+
* Unlike preflight, this hook is async and may do I/O. Resolve names through `ctx`,
|
|
4334
|
+
* the same dispatch context used by execution, never an ambient or cached account.
|
|
4335
|
+
* The host calls it once before raising the ask, enforces the short `ctx.signal`
|
|
4336
|
+
* deadline and discards late answers. A throw yields no summary and a module-attributed
|
|
4337
|
+
* diagnostic; declining costs a click into the app, never an under-described approval.
|
|
4338
|
+
*/
|
|
4339
|
+
describeOperation?(request: {
|
|
4340
|
+
readonly operation: UriOperation;
|
|
4341
|
+
readonly uri: string;
|
|
4342
|
+
readonly payload: unknown;
|
|
4343
|
+
}, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
|
|
4326
4344
|
/**
|
|
4327
4345
|
* Per-resource observability: does a `format: "observation"` read of THIS
|
|
4328
4346
|
* uri render source-native material ({@link UriResolveContext.format})?
|
|
@@ -4453,6 +4471,22 @@ export declare class UriNotFoundError extends UriError {
|
|
|
4453
4471
|
|
|
4454
4472
|
export declare type UriOperation = "help" | "read" | "write" | "edit" | "list" | "search" | "delete";
|
|
4455
4473
|
|
|
4474
|
+
export declare interface UriOperationDescription {
|
|
4475
|
+
/** Static verb phrase authored by the module. Never interpolated from input. */
|
|
4476
|
+
readonly action: string;
|
|
4477
|
+
/** Human name resolved through the connection that will execute the call. */
|
|
4478
|
+
readonly target: string;
|
|
4479
|
+
/** Model-authored content; the host flattens, budgets, elides and bounds it. */
|
|
4480
|
+
readonly quote?: string;
|
|
4481
|
+
/** What this description accounts for; the host refuses undeclared input. */
|
|
4482
|
+
readonly accounts: {
|
|
4483
|
+
/** The payload actually read by the module, after its own decoding. */
|
|
4484
|
+
readonly payload: unknown;
|
|
4485
|
+
readonly payloadFields: readonly string[];
|
|
4486
|
+
readonly queryParams?: readonly string[];
|
|
4487
|
+
};
|
|
4488
|
+
}
|
|
4489
|
+
|
|
4456
4490
|
/**
|
|
4457
4491
|
* Pure, route-aware validation that runs before permission policy can surface an ask.
|
|
4458
4492
|
*
|
package/dist/kay-app.d.ts
CHANGED
|
@@ -1083,18 +1083,15 @@ export declare interface KayAppAutomationArtifactItem {
|
|
|
1083
1083
|
readonly moduleId: string;
|
|
1084
1084
|
/**
|
|
1085
1085
|
* The authoring session. Required on the wire, and load-bearing rather than
|
|
1086
|
-
* decorative: it is the
|
|
1087
|
-
* resolves back to, and the session an `app:artifact:output` uri must be
|
|
1086
|
+
* decorative: it is the session an `app:artifact:output` uri must be
|
|
1088
1087
|
* resolved against to open at all.
|
|
1089
1088
|
*/
|
|
1090
1089
|
readonly sessionId: string;
|
|
1091
1090
|
/** Collapsed by precedence: `deleted` > `created` > `updated`. */
|
|
1092
1091
|
readonly action: "created" | "updated" | "deleted";
|
|
1093
1092
|
/**
|
|
1094
|
-
* One real revision of the group, carrying the action above
|
|
1095
|
-
*
|
|
1096
|
-
* necessarily the newest: the projection stores no ordering for the several
|
|
1097
|
-
* revisions of one target.
|
|
1093
|
+
* One real revision of the group, carrying the action above. Not
|
|
1094
|
+
* necessarily the newest revision of the target.
|
|
1098
1095
|
*/
|
|
1099
1096
|
readonly revision: string;
|
|
1100
1097
|
/** How many revisions this row collapsed. Always at least 1. */
|
|
@@ -1238,7 +1235,7 @@ export declare type KayAppAutomationEffectInput = {
|
|
|
1238
1235
|
* widening of it: an external resource has no revision, no Kay uri module, and
|
|
1239
1236
|
* nothing on this machine to re-read.
|
|
1240
1237
|
*
|
|
1241
|
-
* `kind`
|
|
1238
|
+
* `kind` is the run-detail wire's closed external-resource vocabulary and names
|
|
1242
1239
|
* the host as well as the thing. There is deliberately no `offBranch`: an
|
|
1243
1240
|
* abandoned transcript branch does not un-open a pull request, so branch
|
|
1244
1241
|
* membership is never consulted for these facts.
|
|
@@ -1361,6 +1358,7 @@ export declare interface KayAppAutomationRunOptions {
|
|
|
1361
1358
|
|
|
1362
1359
|
export declare type KayAppAutomationRunOutcome = {
|
|
1363
1360
|
readonly status: "succeeded";
|
|
1361
|
+
readonly summary?: KayAppRunSummary | undefined;
|
|
1364
1362
|
readonly daemonRunId?: string | undefined;
|
|
1365
1363
|
/** Present on a script run: which source this run actually executed. */
|
|
1366
1364
|
readonly script?: {
|
|
@@ -1369,11 +1367,12 @@ export declare type KayAppAutomationRunOutcome = {
|
|
|
1369
1367
|
} | undefined;
|
|
1370
1368
|
} | {
|
|
1371
1369
|
readonly status: "failed";
|
|
1370
|
+
readonly summary?: KayAppRunSummary | undefined;
|
|
1372
1371
|
readonly reason: "executor_failed" | "permission_denied" | "surface_unavailable" | "target_gone";
|
|
1373
1372
|
readonly message?: string | undefined;
|
|
1374
1373
|
} | {
|
|
1375
1374
|
readonly status: "skipped";
|
|
1376
|
-
readonly reason: "already_running" | "binding_inactive" | "circuit_breaker" | "concurrency_capped" | "guard_not_satisfied" | "missed_window";
|
|
1375
|
+
readonly reason: "already_running" | "binding_inactive" | "circuit_breaker" | "concurrency_capped" | "guard_not_satisfied" | "missed_window" | "timer_cancelled";
|
|
1377
1376
|
} | {
|
|
1378
1377
|
readonly status: "abandoned";
|
|
1379
1378
|
readonly reason: "daemon_restart";
|
|
@@ -1407,6 +1406,7 @@ export declare interface KayAppAutomations {
|
|
|
1407
1406
|
get(id: string, ctx: KayAppAutomationContext): Promise<KayAppAutomationBinding | undefined>;
|
|
1408
1407
|
create(input: {
|
|
1409
1408
|
readonly name: string;
|
|
1409
|
+
readonly description?: string;
|
|
1410
1410
|
readonly condition: KayAppAutomationConditionInput;
|
|
1411
1411
|
readonly effect: KayAppAutomationEffectInput;
|
|
1412
1412
|
readonly enabled?: boolean;
|
|
@@ -1468,13 +1468,13 @@ export declare interface KayAppAutomations {
|
|
|
1468
1468
|
readonly nextCursor?: string;
|
|
1469
1469
|
}>;
|
|
1470
1470
|
/**
|
|
1471
|
-
* One run's
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1474
|
-
*
|
|
1475
|
-
*
|
|
1476
|
-
* `
|
|
1477
|
-
*
|
|
1471
|
+
* One run's detail (RFC Release 2): the sessions that run drove (direct and
|
|
1472
|
+
* child, shared-run marked), the artifacts, external resources and skill
|
|
1473
|
+
* suggestions those sessions produced, and explicit coverage and gaps. A
|
|
1474
|
+
* PUBLIC read like {@link listRuns}: it resolves the binding's actual owner
|
|
1475
|
+
* rather than the caller's. Null when no retained run matches `bindingId` +
|
|
1476
|
+
* `runId`. Coverage reports `partial` honestly when some of the run's work
|
|
1477
|
+
* cannot be read — it never blocks or fabricates.
|
|
1478
1478
|
*/
|
|
1479
1479
|
getRun(bindingId: string, runId: string, ctx: KayAppAutomationContext, options?: KayAppAutomationRunDetailOptions): Promise<KayAppAutomationRunDetail | null>;
|
|
1480
1480
|
}
|
|
@@ -1613,10 +1613,15 @@ export declare interface KayAppConfig {
|
|
|
1613
1613
|
* path of the winning file. A JSON-safe twin of the runtime's `KayConfigValueOrigin`
|
|
1614
1614
|
* (`@kay/product-catalog`) — this SDK cannot import that package, so the shape is re-declared
|
|
1615
1615
|
* here and an assignability test in `@kay/agent-runtime` pins the two together. The level union
|
|
1616
|
-
* is the closed, protocol-owned fold precedence stack (`learned < global < project`).
|
|
1616
|
+
* is the closed, protocol-owned fold precedence stack (`baseline < learned < global < project`).
|
|
1617
|
+
* `"baseline"` names a host-composed, in-memory, build/channel-scoped default with no file
|
|
1618
|
+
* behind it (`KayConfigStoreOptions.baselineDefaults`) — a plugin can still be handed one as a
|
|
1619
|
+
* winner for a FIELD within its own declared section that neither it nor the user ever set
|
|
1620
|
+
* (e.g. hindsight's `handlers.signals`, defaulted on by the host for this release channel), so
|
|
1621
|
+
* `path` for that level is a synthetic label, never a real file path.
|
|
1617
1622
|
*/
|
|
1618
1623
|
export declare interface KayAppConfigOrigin {
|
|
1619
|
-
readonly level: "learned" | "global" | "project";
|
|
1624
|
+
readonly level: "baseline" | "learned" | "global" | "project";
|
|
1620
1625
|
readonly path: string;
|
|
1621
1626
|
}
|
|
1622
1627
|
|
|
@@ -1683,7 +1688,10 @@ export declare interface KayAppContextContributor {
|
|
|
1683
1688
|
export declare interface KayAppCorpus {
|
|
1684
1689
|
/** Enumerate every stored session, newest-first. */
|
|
1685
1690
|
listSessions(): AsyncIterable<KayAppCorpusSession>;
|
|
1686
|
-
/**
|
|
1691
|
+
/**
|
|
1692
|
+
* Stream one session's full entry log oldest→newest; an unknown id yields nothing. Treat the
|
|
1693
|
+
* entries as read-only: for a session the daemon holds in memory they are its live objects.
|
|
1694
|
+
*/
|
|
1687
1695
|
readEntries(sessionId: string): AsyncIterable<SessionTreeEntry>;
|
|
1688
1696
|
/**
|
|
1689
1697
|
* Read a cheap transcript change marker without loading the entry log. Unknown or deleted
|
|
@@ -2452,9 +2460,9 @@ export declare interface KayAppPluginEvents {
|
|
|
2452
2460
|
export declare type KayAppProcessingSpeed = "standard" | "fast";
|
|
2453
2461
|
|
|
2454
2462
|
/**
|
|
2455
|
-
* A gap reason on the run-detail wire
|
|
2456
|
-
*
|
|
2457
|
-
*
|
|
2463
|
+
* A gap reason on the run-detail wire: something about the run's work the
|
|
2464
|
+
* daemon could not establish from its session transcripts. The vocabulary is
|
|
2465
|
+
* closed; a daemon may emit only some of these codes.
|
|
2458
2466
|
*/
|
|
2459
2467
|
export declare type KayAppProvenanceGapCode = "legacy-run-id-missing" | "mutation-revision-missing" | "partial-git-provenance" | "git-session-stamp-missing"
|
|
2460
2468
|
/** A run created something off this machine that the transcript cannot name. */
|
|
@@ -2537,6 +2545,14 @@ export declare interface KayAppResolvedCredential {
|
|
|
2537
2545
|
readonly fields?: Readonly<Record<string, string>>;
|
|
2538
2546
|
}
|
|
2539
2547
|
|
|
2548
|
+
export declare interface KayAppRunSummary {
|
|
2549
|
+
/** One plain-text line, at most 100 characters. */
|
|
2550
|
+
readonly headline: string;
|
|
2551
|
+
/** Optional plain-text explanation, at most 600 characters. */
|
|
2552
|
+
readonly details?: string | undefined;
|
|
2553
|
+
readonly author: "automation" | "kay";
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2540
2556
|
/**
|
|
2541
2557
|
* The session provenance vocabulary, as the plugin protocol declares it. These used to be spelled
|
|
2542
2558
|
* out here because the canonical schemas lived in `@kay/agent-runtime-protocol`, which this
|
|
@@ -3006,8 +3022,9 @@ export declare interface KayAppSignalDetector {
|
|
|
3006
3022
|
* - `classified`: Jev answered; `answers`, `model`, and `usage` are the vendor's verbatim
|
|
3007
3023
|
* response, and `stored` says whether the row landed in the durable store.
|
|
3008
3024
|
* - `unavailable`: Jev could not answer (no key, refused grant, timeout, vendor error, or a
|
|
3009
|
-
* malformed response)
|
|
3010
|
-
*
|
|
3025
|
+
* malformed response), or the host has no signals store at all (`reason: "no-store"`).
|
|
3026
|
+
* `reason` names the host's classification; no row is written, so the same input can be
|
|
3027
|
+
* observed again later without a conflict.
|
|
3011
3028
|
* - `invalid`: the input breaks the recorded-name grammar (see {@link invalidObservation}), and
|
|
3012
3029
|
* `reason` says which name and why. The caller's mistake, not the environment's: Jev is never
|
|
3013
3030
|
* called, no row is written, and the same input is refused again. `detector` and `source`
|
|
@@ -3030,7 +3047,11 @@ export declare interface KayAppSignalObservation {
|
|
|
3030
3047
|
} | undefined;
|
|
3031
3048
|
readonly tookMs?: number | undefined;
|
|
3032
3049
|
readonly reason?: string | undefined;
|
|
3033
|
-
/**
|
|
3050
|
+
/**
|
|
3051
|
+
* True when the row landed in the durable store; false when a composed store's own write
|
|
3052
|
+
* failed after Jev already answered (rare -- a host with no store at all instead answers
|
|
3053
|
+
* `unavailable` with reason `"no-store"`, never reaching `classified`).
|
|
3054
|
+
*/
|
|
3034
3055
|
readonly stored: boolean;
|
|
3035
3056
|
/** True when this call returned an already-stored row instead of running inference. */
|
|
3036
3057
|
readonly deduplicated: boolean;
|
|
@@ -3531,10 +3552,22 @@ export declare interface KayAppWorkflowContext {
|
|
|
3531
3552
|
|
|
3532
3553
|
export declare interface KayAppWorkflowDefinition {
|
|
3533
3554
|
readonly name: string;
|
|
3555
|
+
/** Plain-text purpose, at most 280 characters. */
|
|
3556
|
+
readonly description: string;
|
|
3557
|
+
readonly instructions?: readonly KayAppWorkflowInstruction[];
|
|
3558
|
+
readonly perRunNote?: string;
|
|
3534
3559
|
readonly trigger: Extract<KayAppAutomationConditionInput, {
|
|
3535
3560
|
readonly kind: "time";
|
|
3536
3561
|
}>;
|
|
3537
|
-
readonly run: (context: KayAppWorkflowContext) =>
|
|
3562
|
+
readonly run: (context: KayAppWorkflowContext) => Promise<{
|
|
3563
|
+
readonly summary: KayAppRunSummary;
|
|
3564
|
+
}>;
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3567
|
+
/** Fail a workflow while retaining its own explanation beside this attempt. */
|
|
3568
|
+
export declare class KayAppWorkflowFailure extends Error {
|
|
3569
|
+
readonly summary: KayAppRunSummary;
|
|
3570
|
+
constructor(summary: Omit<KayAppRunSummary, "author">, options?: ErrorOptions);
|
|
3538
3571
|
}
|
|
3539
3572
|
|
|
3540
3573
|
export declare interface KayAppWorkflowInspection {
|
|
@@ -3548,6 +3581,20 @@ export declare interface KayAppWorkflowInspectOptions {
|
|
|
3548
3581
|
readonly limit?: number;
|
|
3549
3582
|
}
|
|
3550
3583
|
|
|
3584
|
+
export declare type KayAppWorkflowInstruction = {
|
|
3585
|
+
readonly label: string;
|
|
3586
|
+
readonly kind: "prompt";
|
|
3587
|
+
readonly text: string;
|
|
3588
|
+
} | {
|
|
3589
|
+
readonly label: string;
|
|
3590
|
+
readonly kind: "agent";
|
|
3591
|
+
readonly agent: KayAppAgentDefinitionRef;
|
|
3592
|
+
} | {
|
|
3593
|
+
readonly label: string;
|
|
3594
|
+
readonly kind: "script";
|
|
3595
|
+
readonly ref: KayAppAutomationScriptRef;
|
|
3596
|
+
};
|
|
3597
|
+
|
|
3551
3598
|
export declare interface KayAppWorkflows {
|
|
3552
3599
|
define(localId: string, definition: KayAppWorkflowDefinition): void;
|
|
3553
3600
|
inspect(localId: string, options?: KayAppWorkflowInspectOptions): Promise<KayAppWorkflowInspection>;
|
|
@@ -5412,6 +5459,17 @@ export declare interface UriModule {
|
|
|
5412
5459
|
readonly permission?: UriOperationPermissionPolicy;
|
|
5413
5460
|
/** Pure route/payload validation applied before a consequential permission ask. */
|
|
5414
5461
|
readonly preflight?: UriOperationPreflightPolicy;
|
|
5462
|
+
/**
|
|
5463
|
+
* Supply structured action facts and explicit input coverage for a one-line approval.
|
|
5464
|
+
* The host composes and admits the line; see the protocol's UriModule contract.
|
|
5465
|
+
* Names must be resolved through this call's dispatch context. The host enforces
|
|
5466
|
+
* `ctx.signal`'s deadline and treats a throw or late answer as no summary.
|
|
5467
|
+
*/
|
|
5468
|
+
describeOperation?(request: {
|
|
5469
|
+
readonly operation: UriOperation;
|
|
5470
|
+
readonly uri: string;
|
|
5471
|
+
readonly payload: unknown;
|
|
5472
|
+
}, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
|
|
5415
5473
|
/**
|
|
5416
5474
|
* Set false when the generic write tool must reject `source` copies before
|
|
5417
5475
|
* fetching the source. Inline `content` writes remain available.
|
|
@@ -5584,6 +5642,22 @@ export declare class UriNotFoundError extends UriError {
|
|
|
5584
5642
|
|
|
5585
5643
|
declare type UriOperation = "help" | "read" | "write" | "edit" | "list" | "search" | "delete";
|
|
5586
5644
|
|
|
5645
|
+
declare interface UriOperationDescription {
|
|
5646
|
+
/** Static verb phrase authored by the module. Never interpolated from input. */
|
|
5647
|
+
readonly action: string;
|
|
5648
|
+
/** Human name resolved through the connection that will execute the call. */
|
|
5649
|
+
readonly target: string;
|
|
5650
|
+
/** Model-authored content; the host flattens, budgets, elides and bounds it. */
|
|
5651
|
+
readonly quote?: string;
|
|
5652
|
+
/** What this description accounts for; the host refuses undeclared input. */
|
|
5653
|
+
readonly accounts: {
|
|
5654
|
+
/** The payload actually read by the module, after its own decoding. */
|
|
5655
|
+
readonly payload: unknown;
|
|
5656
|
+
readonly payloadFields: readonly string[];
|
|
5657
|
+
readonly queryParams?: readonly string[];
|
|
5658
|
+
};
|
|
5659
|
+
}
|
|
5660
|
+
|
|
5587
5661
|
declare interface UriOperationPermissionClassification {
|
|
5588
5662
|
/**
|
|
5589
5663
|
* The operation's real effect. The permission classifier only accepts a
|
package/dist/kay-app.js
CHANGED
|
@@ -387,6 +387,16 @@ function transcriptRoleLabel(role) {
|
|
|
387
387
|
return role === "user" ? "User" : "Assistant";
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
+
// packages/plugin-sdk/dist/kay-app/workflows.js
|
|
391
|
+
var KayAppWorkflowFailure = class extends Error {
|
|
392
|
+
summary;
|
|
393
|
+
constructor(summary, options) {
|
|
394
|
+
super(summary.details ?? summary.headline, options);
|
|
395
|
+
this.name = "KayAppWorkflowFailure";
|
|
396
|
+
this.summary = { ...summary, author: "automation" };
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
390
400
|
// packages/plugin-protocol/dist/app-data.js
|
|
391
401
|
var KAY_APP_DATA_SNAPSHOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
392
402
|
var KAY_APP_DATA_CHANGE_MAX_BYTES = 256 * 1024;
|
|
@@ -406,6 +416,7 @@ var KAY_APP_AUTOMATION_CONDITION_KINDS = [
|
|
|
406
416
|
export {
|
|
407
417
|
KAY_APP_AUTOMATION_CONDITION_KINDS,
|
|
408
418
|
KAY_APP_DATA_CHANGE_MAX_BYTES,
|
|
419
|
+
KayAppWorkflowFailure,
|
|
409
420
|
MAX_CHOICE_LABELS,
|
|
410
421
|
SIGNAL_TIME_MAX_MS,
|
|
411
422
|
UriConflictError,
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import type { ChoiceResponse } from '@typesafe-ai/sdk';
|
|
2
|
+
import type { NoulResponse } from '@typesafe-ai/sdk';
|
|
3
|
+
import type { Questions } from '@typesafe-ai/sdk';
|
|
4
|
+
import type { ScoreResponse } from '@typesafe-ai/sdk';
|
|
5
|
+
|
|
6
|
+
export { ChoiceResponse }
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The `signals` a host with no Jev lane or no signals store binds: `observe` never touches the
|
|
10
|
+
* network, answers `invalid` for an input that breaks the recorded-name grammar exactly as a
|
|
11
|
+
* real host would, and otherwise `unavailable` with `stored: false`; both reads answer
|
|
12
|
+
* `unavailable` with reason `no-store`. So a caller's result handling does not change with host
|
|
13
|
+
* composition.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createFallbackOnlySignals(): KayAppSignals;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The `invalid` observation `observe` answers when a name in `input` breaks the recorded-name
|
|
19
|
+
* grammar, or `undefined` when every name can be recorded. A reader later types these names into
|
|
20
|
+
* an address or a `where` filter, so a row that broke the grammar would be unaddressable or
|
|
21
|
+
* unfilterable forever: the grammars are on {@link KayAppSignalsDetectorInput},
|
|
22
|
+
* {@link KayAppSignalSource}, and {@link KayAppSignalsObserveInput}. Checked first, before any
|
|
23
|
+
* digest, grant, store, or Jev call, by the host and by {@link createFallbackOnlySignals} alike.
|
|
24
|
+
*/
|
|
25
|
+
export declare function invalidObservation(input: KayAppSignalsObserveInput): KayAppSignalObservation | undefined;
|
|
26
|
+
|
|
27
|
+
/** One stored signal: the answers exactly as Jev gave them, and where the observed turn is. */
|
|
28
|
+
export declare interface KayAppSignal {
|
|
29
|
+
/** Recording order: a higher id was recorded later. */
|
|
30
|
+
readonly id: number;
|
|
31
|
+
readonly detector: KayAppSignalsDetectorRef & {
|
|
32
|
+
readonly pluginId: string;
|
|
33
|
+
};
|
|
34
|
+
readonly source: KayAppSignalSource;
|
|
35
|
+
readonly recordedAtMs: number;
|
|
36
|
+
/** Keyed by question name, full probability distributions included. */
|
|
37
|
+
readonly answers: Readonly<Record<string, NoulResponse | ChoiceResponse | ScoreResponse>>;
|
|
38
|
+
/** The inference that produced the answers; absent when the host recorded only part of it. */
|
|
39
|
+
readonly inference?: {
|
|
40
|
+
readonly model: string;
|
|
41
|
+
readonly inputTokens: number;
|
|
42
|
+
readonly outputTokens: number;
|
|
43
|
+
readonly tookMs: number;
|
|
44
|
+
};
|
|
45
|
+
/** Digest of the state Jev read: re-derive the state from the transcript and compare. */
|
|
46
|
+
readonly inputDigest: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One version of one detector, under one rubric, as the catalog lists it. */
|
|
50
|
+
export declare interface KayAppSignalDetector {
|
|
51
|
+
readonly pluginId: string;
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly version: string;
|
|
54
|
+
/**
|
|
55
|
+
* The questions never change under one digest, so cache them by it. Two digests under one
|
|
56
|
+
* version mean the rubric was edited without a version bump.
|
|
57
|
+
*/
|
|
58
|
+
readonly rubricDigest: string;
|
|
59
|
+
readonly questions: Questions;
|
|
60
|
+
readonly firstRecordedAtMs: number;
|
|
61
|
+
readonly lastRecordedAtMs: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The result of one observe call. `status` is the only field a caller must switch on:
|
|
66
|
+
*
|
|
67
|
+
* - `classified`: Jev answered; `answers`, `model`, and `usage` are the vendor's verbatim
|
|
68
|
+
* response, and `stored` says whether the row landed in the durable store.
|
|
69
|
+
* - `unavailable`: Jev could not answer (no key, refused grant, timeout, vendor error, or a
|
|
70
|
+
* malformed response), or the host has no signals store at all (`reason: "no-store"`).
|
|
71
|
+
* `reason` names the host's classification; no row is written, so the same input can be
|
|
72
|
+
* observed again later without a conflict.
|
|
73
|
+
* - `invalid`: the input breaks the recorded-name grammar (see {@link invalidObservation}), and
|
|
74
|
+
* `reason` says which name and why. The caller's mistake, not the environment's: Jev is never
|
|
75
|
+
* called, no row is written, and the same input is refused again. `detector` and `source`
|
|
76
|
+
* echo only the detector id and version and the session and entry ids, each empty when the
|
|
77
|
+
* input did not carry it as a string.
|
|
78
|
+
*
|
|
79
|
+
* A repeat call over the same (plugin, detector, version, session, entry, input digest) returns
|
|
80
|
+
* the ALREADY-STORED row with `deduplicated: true` and never calls Jev again: replay is free.
|
|
81
|
+
*/
|
|
82
|
+
export declare interface KayAppSignalObservation {
|
|
83
|
+
readonly id?: number | undefined;
|
|
84
|
+
readonly status: "classified" | "unavailable" | "invalid";
|
|
85
|
+
readonly detector: KayAppSignalsDetectorRef;
|
|
86
|
+
readonly source: KayAppSignalSource;
|
|
87
|
+
readonly answers?: Record<string, unknown> | undefined;
|
|
88
|
+
readonly model?: string | undefined;
|
|
89
|
+
readonly usage?: {
|
|
90
|
+
readonly inputTokens: number;
|
|
91
|
+
readonly outputTokens: number;
|
|
92
|
+
} | undefined;
|
|
93
|
+
readonly tookMs?: number | undefined;
|
|
94
|
+
readonly reason?: string | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* True when the row landed in the durable store; false when a composed store's own write
|
|
97
|
+
* failed after Jev already answered (rare -- a host with no store at all instead answers
|
|
98
|
+
* `unavailable` with reason `"no-store"`, never reaching `classified`).
|
|
99
|
+
*/
|
|
100
|
+
readonly stored: boolean;
|
|
101
|
+
/** True when this call returned an already-stored row instead of running inference. */
|
|
102
|
+
readonly deduplicated: boolean;
|
|
103
|
+
readonly inputDigest: string;
|
|
104
|
+
readonly recordedAtMs: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** One page of signals, oldest recorded first. */
|
|
108
|
+
export declare interface KayAppSignalPage {
|
|
109
|
+
readonly signals: readonly KayAppSignal[];
|
|
110
|
+
/**
|
|
111
|
+
* Pass as `after` to continue, now or on a later run: the next read returns only signals
|
|
112
|
+
* recorded after this page, including any a detector backfilled since. Absent only when this
|
|
113
|
+
* page is empty, which means nothing newer than `after` exists right now; keep the cursor you
|
|
114
|
+
* already have.
|
|
115
|
+
*/
|
|
116
|
+
readonly nextCursor?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The plugin's signal door. `observe` runs the fused inference-and-record step; `list` and
|
|
121
|
+
* `listDetectors` read recorded signals back. The plugin id is stamped by the host at the
|
|
122
|
+
* boundary and is never a parameter.
|
|
123
|
+
*
|
|
124
|
+
* Reads are checked against the `signals.read` claim on every call, and a granted plugin reads
|
|
125
|
+
* EVERY plugin's signals, not only its own.
|
|
126
|
+
*/
|
|
127
|
+
export declare interface KayAppSignals {
|
|
128
|
+
observe(input: KayAppSignalsObserveInput): Promise<KayAppSignalObservation>;
|
|
129
|
+
/**
|
|
130
|
+
* Every detector that has recorded a signal: one entry per version and rubric, each with its
|
|
131
|
+
* questions. Call it once and cache questions by `rubricDigest`; result pages never repeat
|
|
132
|
+
* them.
|
|
133
|
+
*/
|
|
134
|
+
listDetectors(): Promise<KayAppSignalsRead<{
|
|
135
|
+
readonly detectors: readonly KayAppSignalDetector[];
|
|
136
|
+
}>>;
|
|
137
|
+
/** Stored signals for one session, or for one detector across sessions, oldest first. */
|
|
138
|
+
list(query: KayAppSignalsQuery): Promise<KayAppSignalsRead<KayAppSignalPage>>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* One condition on one question's stored answer. Jev answers are probabilities, so each kind of
|
|
143
|
+
* question gets the comparison that makes sense for it:
|
|
144
|
+
*
|
|
145
|
+
* - `yes`: a yes/no question's probability of yes, from 0 to 1.
|
|
146
|
+
* - `choice`: a choice question's chosen label is one of these, with at least `minConfidence`
|
|
147
|
+
* (0 to 1) when given. At most {@link MAX_CHOICE_LABELS} labels, repeats included; a repeat
|
|
148
|
+
* changes nothing else.
|
|
149
|
+
* - `score`: a score question's expected score.
|
|
150
|
+
*
|
|
151
|
+
* The filter is refused as `invalid`, never answered with a silently empty page, when no
|
|
152
|
+
* version of the detector in the query's scope asks the question with the filter's kind, or when
|
|
153
|
+
* a `choice` filter names a label the rubric does not offer. A filter matches only rows whose
|
|
154
|
+
* stored answer to the question is of its own kind, so rows from a version where the question
|
|
155
|
+
* is absent or asks another kind never match; pin `version` to read one version.
|
|
156
|
+
*/
|
|
157
|
+
export declare type KayAppSignalsAnswerFilter = {
|
|
158
|
+
readonly question: string;
|
|
159
|
+
readonly yes: KayAppSignalsRange;
|
|
160
|
+
} | {
|
|
161
|
+
readonly question: string;
|
|
162
|
+
readonly choice: readonly string[];
|
|
163
|
+
readonly minConfidence?: number;
|
|
164
|
+
} | {
|
|
165
|
+
readonly question: string;
|
|
166
|
+
readonly score: KayAppSignalsRange;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* One detector as the recording plugin names it. The host never interprets these values;
|
|
171
|
+
* `id` + `version` are the plugin's half of the versioning contract that lets a consumer tell
|
|
172
|
+
* one rubric's answers from another's. The other half, the rubric digest, is the host's: it is
|
|
173
|
+
* computed from the `questions` actually sent, so a plugin can neither mis-stamp it nor forget
|
|
174
|
+
* it, and a rubric edit that did not bump `version` still shows up as a digest change.
|
|
175
|
+
*/
|
|
176
|
+
export declare interface KayAppSignalsDetectorInput {
|
|
177
|
+
/**
|
|
178
|
+
* Stable identifier, e.g. `memory-curator-gate`: a lowercase slug (a-z and 0-9 in words joined
|
|
179
|
+
* by single hyphens), at most 64 characters, plugin-scoped. It becomes a `signals://` path
|
|
180
|
+
* segment.
|
|
181
|
+
*/
|
|
182
|
+
readonly id: string;
|
|
183
|
+
/**
|
|
184
|
+
* The version the plugin bumps when its rubric's meaning changes, e.g. `v1.0.0`: letters and
|
|
185
|
+
* digits in parts joined by single `.`, `-`, or `_`, at most 64 characters.
|
|
186
|
+
*/
|
|
187
|
+
readonly version: string;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* A detector a read names. `pluginId` is required because detector ids are unique only within
|
|
192
|
+
* the plugin that records them. Omit `version` to read every version; each returned signal
|
|
193
|
+
* carries the version it was recorded under.
|
|
194
|
+
*/
|
|
195
|
+
export declare interface KayAppSignalsDetectorMatch {
|
|
196
|
+
readonly pluginId: string;
|
|
197
|
+
readonly id: string;
|
|
198
|
+
readonly version?: string;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** One detector's signals across every session, optionally in a turn-time window and filtered. */
|
|
202
|
+
export declare interface KayAppSignalsDetectorQuery {
|
|
203
|
+
readonly detector: KayAppSignalsDetectorMatch;
|
|
204
|
+
/** Never both anchors: a detector query reads that detector across every session. */
|
|
205
|
+
readonly sessionId?: never;
|
|
206
|
+
/**
|
|
207
|
+
* Epoch ms, inclusive, on TURN time: when the observed turn settled, or, for a signal whose
|
|
208
|
+
* recorder did not know that, when it was recorded. A detector backfilled over old history
|
|
209
|
+
* therefore places each signal on the day its turn happened. A whole number from 0 to
|
|
210
|
+
* {@link SIGNAL_TIME_MAX_MS}.
|
|
211
|
+
*/
|
|
212
|
+
readonly since?: number;
|
|
213
|
+
/** Epoch ms, exclusive, on turn time, in the same range as `since`. */
|
|
214
|
+
readonly until?: number;
|
|
215
|
+
/** Every filter must hold. At most 20. */
|
|
216
|
+
readonly where?: readonly KayAppSignalsAnswerFilter[];
|
|
217
|
+
/** A `nextCursor` from an earlier page: only signals recorded after it are returned. */
|
|
218
|
+
readonly after?: string;
|
|
219
|
+
/** Default 200, at most 1000 (a larger value is lowered to 1000). */
|
|
220
|
+
readonly limit?: number;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** A detector as a recorded row names it: the plugin's id and version plus the host's digest. */
|
|
224
|
+
export declare interface KayAppSignalsDetectorRef extends KayAppSignalsDetectorInput {
|
|
225
|
+
/**
|
|
226
|
+
* `sha256:<hex>` over the questions serialised with sorted keys, stamped by the host. Empty
|
|
227
|
+
* when nothing was stamped: on a fallback-only host, and on an `invalid` observation.
|
|
228
|
+
*/
|
|
229
|
+
readonly rubricDigest: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export declare interface KayAppSignalsObserveInput {
|
|
233
|
+
readonly detector: KayAppSignalsDetectorInput;
|
|
234
|
+
readonly source: KayAppSignalSource;
|
|
235
|
+
/**
|
|
236
|
+
* The exact state to send, already shaped by the caller (a turn packet, a window of messages,
|
|
237
|
+
* or anything else). NOT stored: the row keeps its digest, and a reader re-derives it from the
|
|
238
|
+
* transcript the source points at. So the projection must be a pure function of the
|
|
239
|
+
* transcript and the detector version, or the row's evidence cannot be reproduced.
|
|
240
|
+
*/
|
|
241
|
+
readonly state: unknown;
|
|
242
|
+
/**
|
|
243
|
+
* The typed questions to answer, as the vendor SDK accepts them. Stored once per digest. A
|
|
244
|
+
* rubric asks 1 to 64 questions. Each name is an identifier (letters, digits, and `_`, not
|
|
245
|
+
* starting with a digit), at most 64 characters, because a reader types it into a `where`
|
|
246
|
+
* filter. A choice question offers at most {@link MAX_CHOICE_LABELS} labels, each 1 to 64
|
|
247
|
+
* characters with no `,`, `@`, or control or invisible character (`\p{C}`), and no whitespace
|
|
248
|
+
* but single spaces between words, so a reader can copy it back exactly from rendered text.
|
|
249
|
+
*/
|
|
250
|
+
readonly questions: Questions;
|
|
251
|
+
readonly options?: WithJevOptions;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Where the observation came from: a pointer into the transcript, which is the only thing the
|
|
256
|
+
* row keeps of the state. The settle anchor is the entry the session event named; it may be a
|
|
257
|
+
* post-run bookkeeping entry (a settle event points at the run's last entry, not necessarily
|
|
258
|
+
* the opening message), so the packet helper folds it back to the turn it follows and reports
|
|
259
|
+
* that turn's opener here.
|
|
260
|
+
*/
|
|
261
|
+
export declare interface KayAppSignalSource {
|
|
262
|
+
/**
|
|
263
|
+
* The session, settle, and opener ids are each 1 to 256 characters with no whitespace, `/`,
|
|
264
|
+
* `?`, `#`, or control or invisible character (`\p{C}`), so each can be named in an address
|
|
265
|
+
* and renders exactly as stored.
|
|
266
|
+
*/
|
|
267
|
+
readonly sessionId: string;
|
|
268
|
+
/** The settle anchor: a member of the dedupe key. */
|
|
269
|
+
readonly entryId: string;
|
|
270
|
+
/** The message that opened the observed turn, when the caller's projection has one. */
|
|
271
|
+
readonly openerEntryId?: string;
|
|
272
|
+
/**
|
|
273
|
+
* The daemon run that produced the settle: 1 to 256 characters with no control character.
|
|
274
|
+
* No reader types it into an address or a filter, so nothing else about it is constrained.
|
|
275
|
+
*/
|
|
276
|
+
readonly runId?: string;
|
|
277
|
+
/**
|
|
278
|
+
* Epoch ms of the settle anchor, when the caller knows it: a whole number from 0 to
|
|
279
|
+
* {@link SIGNAL_TIME_MAX_MS}.
|
|
280
|
+
*/
|
|
281
|
+
readonly occurredAtMs?: number;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Every read names one session or one detector. There is no read across every detector at
|
|
286
|
+
* once, because its cost grows with every detector anyone adds; the host refuses a query that
|
|
287
|
+
* names neither, or both, at runtime as well as in the type.
|
|
288
|
+
*/
|
|
289
|
+
export declare type KayAppSignalsQuery = KayAppSignalsSessionQuery | KayAppSignalsDetectorQuery;
|
|
290
|
+
|
|
291
|
+
/** A numeric range. An absent bound is open; a present bound is inclusive. */
|
|
292
|
+
export declare interface KayAppSignalsRange {
|
|
293
|
+
readonly atLeast?: number;
|
|
294
|
+
readonly atMost?: number;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The outcome of a read. Like `observe`, a read never rejects while the plugin is enabled; a
|
|
299
|
+
* handle kept after the plugin is disabled throws synchronously instead.
|
|
300
|
+
*
|
|
301
|
+
* - `ok`: the result.
|
|
302
|
+
* - `unavailable`: this host cannot serve reads right now. `reason` is `not-granted` (the
|
|
303
|
+
* `signals.read` claim was refused), `no-store` (this host records no signals), or
|
|
304
|
+
* `store-unavailable` (the store failed). Asking again later is reasonable.
|
|
305
|
+
* - `invalid`: the host refused the query itself, and `reason` says why in plain words: for
|
|
306
|
+
* example a question the detector's rubric does not ask, a filter of the wrong kind for its
|
|
307
|
+
* question, or a cursor this host did not issue. The same query will be refused again.
|
|
308
|
+
*/
|
|
309
|
+
export declare type KayAppSignalsRead<T> = ({
|
|
310
|
+
readonly status: "ok";
|
|
311
|
+
} & T) | {
|
|
312
|
+
readonly status: "unavailable";
|
|
313
|
+
readonly reason: string;
|
|
314
|
+
} | {
|
|
315
|
+
readonly status: "invalid";
|
|
316
|
+
readonly reason: string;
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
/** One session's signals: every detector's, or only the detectors named. */
|
|
320
|
+
export declare interface KayAppSignalsSessionQuery {
|
|
321
|
+
readonly sessionId: string;
|
|
322
|
+
/** Never both anchors: a session query names detectors through `detectors`. */
|
|
323
|
+
readonly detector?: never;
|
|
324
|
+
/** Omit for every detector. At most 100, and never empty. */
|
|
325
|
+
readonly detectors?: readonly KayAppSignalsDetectorMatch[];
|
|
326
|
+
/** A `nextCursor` from an earlier page: only signals recorded after it are returned. */
|
|
327
|
+
readonly after?: string;
|
|
328
|
+
/** Default 200, at most 1000 (a larger value is lowered to 1000). */
|
|
329
|
+
readonly limit?: number;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** The most labels a recorded choice question may offer, and so the most one filter names. */
|
|
333
|
+
export declare const MAX_CHOICE_LABELS = 64;
|
|
334
|
+
|
|
335
|
+
export { NoulResponse }
|
|
336
|
+
|
|
337
|
+
export { ScoreResponse }
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* The largest epoch-millisecond time a `Date` can hold (about the year 275,760). A time outside
|
|
341
|
+
* 0 to this bound has no date to show, so neither `observe` nor a read window accepts one.
|
|
342
|
+
*/
|
|
343
|
+
export declare const SIGNAL_TIME_MAX_MS = 8640000000000000;
|
|
344
|
+
|
|
345
|
+
/** Per-call knobs for {@link WithJev}. Every field is optional; the host applies its own default
|
|
346
|
+
* budget when none is given, so the Jev branch is never unbounded. */
|
|
347
|
+
declare interface WithJevOptions {
|
|
348
|
+
/**
|
|
349
|
+
* Explicit cancellation. Always propagates as a rejection; never substituted with `fallback`,
|
|
350
|
+
* because an aborted call means "stop", not "use the cheaper path".
|
|
351
|
+
*/
|
|
352
|
+
readonly signal?: AbortSignal;
|
|
353
|
+
/** Bounds the Jev branch alone, measured from the call. */
|
|
354
|
+
readonly timeoutMs?: number;
|
|
355
|
+
/**
|
|
356
|
+
* Absolute epoch-ms hard deadline for the WHOLE call, `fallback` included. A deadline already
|
|
357
|
+
* passed rejects (like `signal`); one still ahead derives the Jev branch's budget from it and
|
|
358
|
+
* keeps a small reserve so `fallback` has a window to run.
|
|
359
|
+
*/
|
|
360
|
+
readonly deadlineAt?: number;
|
|
361
|
+
/** Attribution slug for host usage accounting. Never sent to Jev. */
|
|
362
|
+
readonly purpose?: string;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export { }
|
package/dist/signals.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// packages/plugin-sdk/dist/signals.js
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
function createFallbackOnlySignals() {
|
|
4
|
+
const noStore = { status: "unavailable", reason: "no-store" };
|
|
5
|
+
return {
|
|
6
|
+
observe: (input) => Promise.resolve(invalidObservation(input) ?? {
|
|
7
|
+
status: "unavailable",
|
|
8
|
+
reason: "no-jev-lane",
|
|
9
|
+
detector: { ...input.detector, rubricDigest: "" },
|
|
10
|
+
source: input.source,
|
|
11
|
+
stored: false,
|
|
12
|
+
deduplicated: false,
|
|
13
|
+
inputDigest: "",
|
|
14
|
+
recordedAtMs: Date.now()
|
|
15
|
+
}),
|
|
16
|
+
listDetectors: () => Promise.resolve(noStore),
|
|
17
|
+
list: () => Promise.resolve(noStore)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
var SIGNAL_TIME_MAX_MS = 864e13;
|
|
21
|
+
var MAX_CHOICE_LABELS = 64;
|
|
22
|
+
var MAX_NAME = 64;
|
|
23
|
+
var MAX_POINTER = 256;
|
|
24
|
+
var MAX_QUESTIONS = 64;
|
|
25
|
+
var DETECTOR_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
26
|
+
var VERSION = /^[A-Za-z0-9]+(?:[._-][A-Za-z0-9]+)*$/u;
|
|
27
|
+
var QUESTION_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
28
|
+
var POINTER = /^[^\s\p{C}/?#]+$/u;
|
|
29
|
+
var LABEL = /^[^\s,@\p{C}]+(?: [^\s,@\p{C}]+)*$/u;
|
|
30
|
+
var RUN_ID = /^\P{Cc}+$/u;
|
|
31
|
+
var POINTER_RULE = `1 to ${MAX_POINTER} characters with no whitespace, \`/\`, \`?\`, \`#\`, or control or invisible character`;
|
|
32
|
+
var ObserveShape = z.looseObject({
|
|
33
|
+
detector: z.looseObject({ id: z.string(), version: z.string() }),
|
|
34
|
+
source: z.looseObject({
|
|
35
|
+
sessionId: z.string(),
|
|
36
|
+
entryId: z.string(),
|
|
37
|
+
openerEntryId: z.string().optional(),
|
|
38
|
+
runId: z.string().optional(),
|
|
39
|
+
occurredAtMs: z.unknown().optional()
|
|
40
|
+
}),
|
|
41
|
+
questions: z.record(z.string(), z.unknown())
|
|
42
|
+
});
|
|
43
|
+
var ChoiceQuestion = z.looseObject({
|
|
44
|
+
type: z.literal("choice"),
|
|
45
|
+
criteria: z.record(z.string(), z.unknown())
|
|
46
|
+
});
|
|
47
|
+
var fits = (value, grammar) => value.length <= MAX_NAME && grammar.test(value);
|
|
48
|
+
var quoted = (value) => JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}\u2026` : value).replace(/(?! )[\s\p{C}]/gu, (char) => `\\u${char.codePointAt(0).toString(16).padStart(4, "0")}`);
|
|
49
|
+
function grammarProblem(input) {
|
|
50
|
+
const shape = ObserveShape.safeParse(input);
|
|
51
|
+
if (!shape.success) {
|
|
52
|
+
const field = shape.error.issues[0]?.path.join(".") ?? "";
|
|
53
|
+
return field === "" ? "`observe` takes an object with `detector`, `source`, `state`, and `questions`." : `\`${field}\` is missing or has the wrong type.`;
|
|
54
|
+
}
|
|
55
|
+
const { detector, source, questions } = shape.data;
|
|
56
|
+
if (!fits(detector.id, DETECTOR_ID)) {
|
|
57
|
+
return "`detector.id` must be a lowercase slug of at most 64 characters: a-z and 0-9 in words joined by single hyphens.";
|
|
58
|
+
}
|
|
59
|
+
if (!fits(detector.version, VERSION)) {
|
|
60
|
+
return "`detector.version` must be at most 64 characters: letters and digits in parts joined by single `.`, `-`, or `_`.";
|
|
61
|
+
}
|
|
62
|
+
for (const [field, grammar, rule] of [
|
|
63
|
+
["sessionId", POINTER, POINTER_RULE],
|
|
64
|
+
["entryId", POINTER, POINTER_RULE],
|
|
65
|
+
["openerEntryId", POINTER, POINTER_RULE],
|
|
66
|
+
[
|
|
67
|
+
"runId",
|
|
68
|
+
RUN_ID,
|
|
69
|
+
`1 to ${MAX_POINTER} characters with no control character`
|
|
70
|
+
]
|
|
71
|
+
]) {
|
|
72
|
+
const id = source[field];
|
|
73
|
+
if (id !== void 0 && !(id.length <= MAX_POINTER && grammar.test(id))) {
|
|
74
|
+
return `\`source.${field}\` must be ${rule}.`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const occurredAtMs = source.occurredAtMs;
|
|
78
|
+
if (occurredAtMs !== void 0 && !(typeof occurredAtMs === "number" && Number.isSafeInteger(occurredAtMs) && occurredAtMs >= 0 && occurredAtMs <= SIGNAL_TIME_MAX_MS)) {
|
|
79
|
+
return `\`source.occurredAtMs\` must be whole epoch milliseconds from 0 to ${SIGNAL_TIME_MAX_MS}.`;
|
|
80
|
+
}
|
|
81
|
+
const names = Object.keys(questions);
|
|
82
|
+
if (names.length === 0 || names.length > MAX_QUESTIONS) {
|
|
83
|
+
return `\`questions\` must ask 1 to ${MAX_QUESTIONS} questions.`;
|
|
84
|
+
}
|
|
85
|
+
for (const name of names) {
|
|
86
|
+
if (!fits(name, QUESTION_NAME)) {
|
|
87
|
+
return `The question name ${quoted(name)} must be an identifier of at most 64 characters: letters, digits, and \`_\`, not starting with a digit.`;
|
|
88
|
+
}
|
|
89
|
+
const choice = ChoiceQuestion.safeParse(questions[name]);
|
|
90
|
+
if (!choice.success)
|
|
91
|
+
continue;
|
|
92
|
+
const labels = Object.keys(choice.data.criteria);
|
|
93
|
+
if (labels.length > MAX_CHOICE_LABELS) {
|
|
94
|
+
return `The choice question \`${name}\` offers ${labels.length} labels; at most ${MAX_CHOICE_LABELS}.`;
|
|
95
|
+
}
|
|
96
|
+
const bad = labels.find((label) => !fits(label, LABEL));
|
|
97
|
+
if (bad !== void 0) {
|
|
98
|
+
return `The choice question \`${name}\` offers the label ${quoted(bad)}, which a \`where\` filter cannot name: a label is 1 to 64 characters with no \`,\`, \`@\`, or control or invisible character, and no whitespace but single spaces between words.`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return void 0;
|
|
102
|
+
}
|
|
103
|
+
var EchoText = z.string().catch("");
|
|
104
|
+
var Echo = z.object({
|
|
105
|
+
detector: z.object({ id: EchoText, version: EchoText }).catch({ id: "", version: "" }),
|
|
106
|
+
source: z.object({ sessionId: EchoText, entryId: EchoText }).catch({ sessionId: "", entryId: "" })
|
|
107
|
+
}).catch({
|
|
108
|
+
detector: { id: "", version: "" },
|
|
109
|
+
source: { sessionId: "", entryId: "" }
|
|
110
|
+
});
|
|
111
|
+
function invalidObservation(input) {
|
|
112
|
+
const reason = grammarProblem(input);
|
|
113
|
+
if (reason === void 0)
|
|
114
|
+
return void 0;
|
|
115
|
+
const { detector, source } = Echo.parse(input);
|
|
116
|
+
return {
|
|
117
|
+
status: "invalid",
|
|
118
|
+
reason,
|
|
119
|
+
detector: { ...detector, rubricDigest: "" },
|
|
120
|
+
source,
|
|
121
|
+
stored: false,
|
|
122
|
+
deduplicated: false,
|
|
123
|
+
inputDigest: "",
|
|
124
|
+
recordedAtMs: Date.now()
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
MAX_CHOICE_LABELS,
|
|
129
|
+
SIGNAL_TIME_MAX_MS,
|
|
130
|
+
createFallbackOnlySignals,
|
|
131
|
+
invalidObservation
|
|
132
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaklabs-ai/plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "The Kay plugin SDK — types and helpers for authoring a Kay directory plugin.",
|
|
5
5
|
"author": "YakLabs",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,14 @@
|
|
|
25
25
|
"types": "./dist/disclosure.d.ts",
|
|
26
26
|
"default": "./dist/disclosure.js"
|
|
27
27
|
},
|
|
28
|
+
"./jev": {
|
|
29
|
+
"types": "./dist/jev.d.ts",
|
|
30
|
+
"default": "./dist/jev.js"
|
|
31
|
+
},
|
|
32
|
+
"./signals": {
|
|
33
|
+
"types": "./dist/signals.d.ts",
|
|
34
|
+
"default": "./dist/signals.js"
|
|
35
|
+
},
|
|
28
36
|
"./phase-shift": {
|
|
29
37
|
"types": "./dist/phase-shift.d.ts",
|
|
30
38
|
"default": "./dist/phase-shift.js"
|