@chelone/core 0.5.0 → 0.7.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/dist/assistant/assistant-session.d.ts +28 -0
- package/dist/assistant/assistant-session.js +208 -0
- package/dist/assistant/echo-transport.d.ts +7 -0
- package/dist/assistant/echo-transport.js +28 -0
- package/dist/assistant/http-transport.d.ts +10 -0
- package/dist/assistant/http-transport.js +98 -0
- package/dist/assistant/types.d.ts +54 -0
- package/dist/auth/access-control.d.ts +13 -5
- package/dist/auth/access-control.js +25 -21
- package/dist/auth/auth-options.d.ts +21 -17
- package/dist/auth/auth-options.js +80 -48
- package/dist/auth/permission-matrix.d.ts +12 -0
- package/dist/auth/permission-matrix.js +67 -0
- package/dist/auth/types.d.ts +14 -4
- package/dist/block-editor/block-document.d.ts +60 -0
- package/dist/block-editor/block-document.js +143 -0
- package/dist/block-editor/data-blocks.d.ts +63 -0
- package/dist/block-editor/data-blocks.js +117 -0
- package/dist/chart/chart-data.d.ts +25 -0
- package/dist/chart/chart-data.js +61 -0
- package/dist/command-menu/hotkey.js +58 -47
- package/dist/command-menu/recents.js +8 -11
- package/dist/command-menu/registry.d.ts +9 -5
- package/dist/command-menu/registry.js +36 -46
- package/dist/controller/calendar-dates.js +82 -113
- package/dist/controller/calendar-events.js +93 -88
- package/dist/controller/client-list-controller.d.ts +2 -2
- package/dist/controller/client-list-controller.js +251 -254
- package/dist/controller/data-table-columns.d.ts +2 -0
- package/dist/controller/data-table-columns.js +50 -56
- package/dist/controller/deep-equal.d.ts +1 -0
- package/dist/controller/deep-equal.js +14 -0
- package/dist/controller/filter-toggle.js +36 -44
- package/dist/controller/in-place-editor.d.ts +42 -0
- package/dist/controller/in-place-editor.js +42 -0
- package/dist/controller/infinite-list-controller.js +220 -190
- package/dist/controller/inspector.d.ts +49 -0
- package/dist/controller/inspector.js +64 -0
- package/dist/controller/job-monitor.d.ts +41 -0
- package/dist/controller/job-monitor.js +49 -0
- package/dist/controller/kanban-board.d.ts +2 -3
- package/dist/controller/kanban-board.js +43 -24
- package/dist/controller/list-controller.d.ts +5 -5
- package/dist/controller/list-controller.js +444 -400
- package/dist/controller/list-params.js +96 -87
- package/dist/controller/pivot-grid.d.ts +36 -0
- package/dist/controller/pivot-grid.js +99 -0
- package/dist/controller/prev-next.d.ts +2 -2
- package/dist/controller/prev-next.js +74 -54
- package/dist/controller/record-controllers.d.ts +14 -6
- package/dist/controller/record-controllers.js +288 -257
- package/dist/controller/record-diff.d.ts +21 -0
- package/dist/controller/record-diff.js +50 -0
- package/dist/controller/record-timeline.d.ts +35 -0
- package/dist/controller/record-timeline.js +48 -0
- package/dist/controller/record-tree.js +57 -47
- package/dist/controller/record-workflow.d.ts +38 -16
- package/dist/controller/record-workflow.js +85 -64
- package/dist/controller/reference-many-controller.js +198 -192
- package/dist/controller/reference-one-controller.d.ts +1 -1
- package/dist/controller/reference-one-controller.js +63 -56
- package/dist/controller/saved-queries.js +104 -91
- package/dist/controller/translate-fallback.js +10 -6
- package/dist/csv/export.d.ts +2 -1
- package/dist/csv/export.js +43 -40
- package/dist/csv/import-controller.js +185 -169
- package/dist/csv/match-headers.js +29 -22
- package/dist/csv/parse-with.js +60 -54
- package/dist/csv/run-import.js +69 -72
- package/dist/csv/validate-rows.d.ts +1 -1
- package/dist/csv/validate-rows.js +65 -52
- package/dist/data/cache-ops.d.ts +5 -4
- package/dist/data/cache-ops.js +208 -190
- package/dist/data/combine-data-providers.js +25 -23
- package/dist/data/data-provider-log.d.ts +45 -0
- package/dist/data/data-provider-log.js +93 -0
- package/dist/data/errors.js +14 -10
- package/dist/data/fake-provider.d.ts +6 -3
- package/dist/data/fake-provider.js +129 -114
- package/dist/data/fetch-utils.js +61 -44
- package/dist/data/filter-branch.d.ts +5 -0
- package/dist/data/filter-branch.js +10 -0
- package/dist/data/get-many-aggregate.js +84 -76
- package/dist/data/http-error.js +17 -12
- package/dist/data/keys.js +50 -36
- package/dist/data/mutation-options.d.ts +14 -9
- package/dist/data/mutation-options.js +93 -109
- package/dist/data/query-options.js +73 -68
- package/dist/data/remove-empty.d.ts +3 -0
- package/dist/data/remove-empty.js +11 -0
- package/dist/data/undoable.d.ts +2 -2
- package/dist/data/undoable.js +190 -174
- package/dist/data/with-lifecycle-callbacks.d.ts +45 -17
- package/dist/data/with-lifecycle-callbacks.js +66 -52
- package/dist/environment/application-update-store.js +58 -55
- package/dist/environment/offline-store.js +21 -21
- package/dist/form/array-binding.d.ts +30 -11
- package/dist/form/array-binding.js +184 -166
- package/dist/form/choice-ids.d.ts +7 -0
- package/dist/form/choice-ids.js +11 -0
- package/dist/form/choices-controller.d.ts +3 -2
- package/dist/form/choices-controller.js +302 -275
- package/dist/form/date-format.d.ts +38 -0
- package/dist/form/date-format.js +89 -0
- package/dist/form/dirty-values.d.ts +2 -2
- package/dist/form/dirty-values.js +22 -11
- package/dist/form/duration.d.ts +22 -0
- package/dist/form/duration.js +49 -0
- package/dist/form/edit-create-form.d.ts +12 -5
- package/dist/form/edit-create-form.js +243 -188
- package/dist/form/event-value.d.ts +6 -0
- package/dist/form/event-value.js +17 -0
- package/dist/form/field-error.d.ts +14 -0
- package/dist/form/field-error.js +28 -0
- package/dist/form/field-name.d.ts +20 -0
- package/dist/form/field-name.js +6 -0
- package/dist/form/filter-form-values.js +63 -49
- package/dist/form/form-controller.d.ts +9 -1
- package/dist/form/form-controller.js +165 -160
- package/dist/form/form-groups.d.ts +4 -3
- package/dist/form/form-groups.js +79 -79
- package/dist/form/form-handle.d.ts +175 -0
- package/dist/form/form-handle.js +68 -0
- package/dist/form/input-binding.d.ts +56 -31
- package/dist/form/input-binding.js +169 -118
- package/dist/form/input-values.d.ts +130 -0
- package/dist/form/input-values.js +177 -0
- package/dist/form/json-text.js +51 -37
- package/dist/form/save-pipeline.d.ts +6 -8
- package/dist/form/save-pipeline.js +91 -82
- package/dist/form/set-form-values.d.ts +6 -0
- package/dist/form/set-form-values.js +10 -0
- package/dist/form/source.js +97 -65
- package/dist/form/text-of.d.ts +2 -0
- package/dist/form/text-of.js +7 -0
- package/dist/form/validators.d.ts +58 -19
- package/dist/form/validators.js +97 -71
- package/dist/geo/bbox.d.ts +5 -4
- package/dist/geo/bbox.js +69 -48
- package/dist/geo/bounds.d.ts +14 -3
- package/dist/geo/bounds.js +98 -68
- package/dist/geo/constraints.js +88 -93
- package/dist/geo/coordinates.js +31 -23
- package/dist/geo/editor.d.ts +13 -13
- package/dist/geo/editor.js +176 -153
- package/dist/geo/geocoding.d.ts +4 -4
- package/dist/geo/geocoding.js +38 -41
- package/dist/geo/geojson.d.ts +12 -74
- package/dist/geo/geojson.js +52 -40
- package/dist/geo/geometry-math.d.ts +4 -4
- package/dist/geo/geometry-math.js +100 -93
- package/dist/geo/index.d.ts +7 -6
- package/dist/geo/map-layers.d.ts +42 -7
- package/dist/geo/map-layers.js +88 -62
- package/dist/geo/map-style.js +31 -32
- package/dist/geo/messages.js +48 -45
- package/dist/geo/nominatim.js +88 -80
- package/dist/geo/osm-presets.d.ts +4 -3
- package/dist/geo/osm-presets.js +108 -86
- package/dist/geo/osm-tags.js +14 -12
- package/dist/geo/overpass.d.ts +14 -2
- package/dist/geo/overpass.js +83 -90
- package/dist/geo/shape-value.d.ts +9 -5
- package/dist/geo/shape-value.js +169 -146
- package/dist/geo/snap-to-roads.d.ts +3 -3
- package/dist/geo/snap-to-roads.js +55 -50
- package/dist/geo/throttle.js +36 -39
- package/dist/guards.d.ts +37 -0
- package/dist/guards.js +62 -0
- package/dist/i18n/default-messages.js +34 -29
- package/dist/i18n/english-messages.js +187 -189
- package/dist/i18n/i18n.d.ts +8 -2
- package/dist/i18n/i18n.js +47 -48
- package/dist/i18n/inflection.d.ts +34 -0
- package/dist/i18n/inflection.js +82 -0
- package/dist/i18n/messages.d.ts +10 -2
- package/dist/i18n/messages.js +17 -4
- package/dist/i18n/missing-keys.d.ts +19 -0
- package/dist/i18n/missing-keys.js +68 -0
- package/dist/i18n/translatable.d.ts +16 -1
- package/dist/i18n/translatable.js +40 -47
- package/dist/index.d.ts +96 -22
- package/dist/index.js +52 -257
- package/dist/json/deep-equal.d.ts +8 -0
- package/dist/json/deep-equal.js +24 -0
- package/dist/json/strip-json-comments.js +38 -42
- package/dist/noop.d.ts +2 -0
- package/dist/noop.js +4 -0
- package/dist/notification/error-message.d.ts +2 -0
- package/dist/notification/error-message.js +10 -0
- package/dist/notification/notification-store.d.ts +3 -2
- package/dist/notification/notification-store.js +41 -45
- package/dist/onboarding-tour/placement.d.ts +34 -0
- package/dist/onboarding-tour/placement.js +67 -0
- package/dist/onboarding-tour/tour-controller.d.ts +25 -0
- package/dist/onboarding-tour/tour-controller.js +50 -0
- package/dist/realtime/add-events-for-mutations.js +75 -81
- package/dist/realtime/announce-lock.d.ts +7 -0
- package/dist/realtime/announce-lock.js +18 -0
- package/dist/realtime/broadcast-channel-transport.d.ts +14 -0
- package/dist/realtime/broadcast-channel-transport.js +69 -0
- package/dist/realtime/copy-members.js +22 -14
- package/dist/realtime/in-memory-lock-provider.js +41 -60
- package/dist/realtime/live.js +95 -101
- package/dist/realtime/lock-options.js +16 -16
- package/dist/realtime/lock-session.js +91 -78
- package/dist/realtime/memory-transport.js +26 -67
- package/dist/realtime/presence.js +120 -122
- package/dist/realtime/realtime-data-provider.d.ts +10 -0
- package/dist/realtime/realtime-data-provider.js +45 -42
- package/dist/realtime/reconnect-backoff.d.ts +15 -0
- package/dist/realtime/reconnect-backoff.js +17 -0
- package/dist/realtime/sse-transport.d.ts +31 -0
- package/dist/realtime/sse-transport.js +235 -0
- package/dist/realtime/status-store.js +23 -28
- package/dist/realtime/topic-hub.d.ts +24 -0
- package/dist/realtime/topic-hub.js +66 -0
- package/dist/realtime/topics.js +10 -12
- package/dist/realtime/types.js +10 -12
- package/dist/realtime/unread-count.js +21 -21
- package/dist/realtime/websocket-transport.d.ts +32 -0
- package/dist/realtime/websocket-transport.js +306 -0
- package/dist/resource/describe-resource.d.ts +2 -0
- package/dist/resource/describe-resource.js +232 -219
- package/dist/resource/get-at-path.js +29 -14
- package/dist/resource/identifier.d.ts +18 -0
- package/dist/resource/identifier.js +27 -0
- package/dist/resource/record.d.ts +4 -0
- package/dist/resource/record.js +10 -0
- package/dist/resource/register.d.ts +7 -0
- package/dist/resource/registry-store.d.ts +45 -15
- package/dist/resource/registry-store.js +67 -60
- package/dist/resource/registry.d.ts +10 -0
- package/dist/resource/registry.js +8 -4
- package/dist/resource/source-path.d.ts +16 -1
- package/dist/rich-text/rich-text-extras.d.ts +132 -0
- package/dist/rich-text/rich-text-extras.js +179 -0
- package/dist/rich-text/rich-text-image.d.ts +124 -0
- package/dist/rich-text/rich-text-image.js +163 -0
- package/dist/rich-text/rich-text.d.ts +62 -0
- package/dist/rich-text/rich-text.js +142 -0
- package/dist/router/location.d.ts +22 -4
- package/dist/router/location.js +41 -18
- package/dist/router/match.js +47 -39
- package/dist/router/paths.js +40 -34
- package/dist/router/record-from-location.d.ts +1 -1
- package/dist/router/record-from-location.js +25 -17
- package/dist/store/adapters.js +51 -55
- package/dist/store/micro-store.js +57 -59
- package/dist/store/shallow-equal.d.ts +12 -0
- package/dist/store/shallow-equal.js +20 -0
- package/dist/store/store.js +54 -57
- package/package.json +13 -11
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type MicroStore } from "../store/micro-store.js";
|
|
2
|
+
import type { AssistantMessage, AssistantTool, AssistantToolCall, AssistantTransport } from "./types.js";
|
|
3
|
+
export interface AssistantSessionState {
|
|
4
|
+
messages: AssistantMessage[];
|
|
5
|
+
status: "idle" | "streaming" | "error";
|
|
6
|
+
error: Error | null;
|
|
7
|
+
}
|
|
8
|
+
export interface CreateAssistantSessionOptions {
|
|
9
|
+
transport: AssistantTransport;
|
|
10
|
+
tools: Record<string, AssistantTool>;
|
|
11
|
+
/** Follow-up turns after tool calls before the session gives up. Default 5. */
|
|
12
|
+
maxToolRounds?: number;
|
|
13
|
+
/** Answers for tools that set `confirm`. Without it they never run. */
|
|
14
|
+
confirmTool?: (call: AssistantToolCall) => boolean | Promise<boolean>;
|
|
15
|
+
}
|
|
16
|
+
export interface AssistantSession extends MicroStore<AssistantSessionState> {
|
|
17
|
+
/** Resolves when the turn, with its tool rounds, ends. */
|
|
18
|
+
send(text: string): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* After an error, runs the last user message again: whatever the failed
|
|
21
|
+
* turn added after it is dropped and no second user message is added.
|
|
22
|
+
* Does nothing unless the status is `error`.
|
|
23
|
+
*/
|
|
24
|
+
retry(): Promise<void>;
|
|
25
|
+
stop(): void;
|
|
26
|
+
reset(): void;
|
|
27
|
+
}
|
|
28
|
+
export declare function createAssistantSession({ transport, tools, maxToolRounds, confirmTool, }: CreateAssistantSessionOptions): AssistantSession;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { createMicroStore } from "../store/micro-store.js";
|
|
2
|
+
// src/assistant/assistant-session.ts
|
|
3
|
+
var toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
4
|
+
var issuesText = (issues) => issues.map((issue) => issue.message).join("; ");
|
|
5
|
+
/** Resolves with `undefined` when the signal aborts before the promise settles. */
|
|
6
|
+
function raceAbort(promise, signal) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const onAbort = () => resolve(void 0);
|
|
9
|
+
if (signal.aborted) return onAbort();
|
|
10
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
11
|
+
promise.then(resolve, reject).finally(() => {
|
|
12
|
+
signal.removeEventListener("abort", onAbort);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function createAssistantSession({ transport, tools, maxToolRounds = 5, confirmTool }) {
|
|
17
|
+
const store = createMicroStore({
|
|
18
|
+
messages: [],
|
|
19
|
+
status: "idle",
|
|
20
|
+
error: null
|
|
21
|
+
});
|
|
22
|
+
const declarations = Object.fromEntries(Object.entries(tools).map(([name, { description, parameters }]) => [name, {
|
|
23
|
+
description,
|
|
24
|
+
parameters
|
|
25
|
+
}]));
|
|
26
|
+
let active = null;
|
|
27
|
+
let destroyed = false;
|
|
28
|
+
store.onDestroy(() => {
|
|
29
|
+
destroyed = true;
|
|
30
|
+
active?.abort();
|
|
31
|
+
active = null;
|
|
32
|
+
});
|
|
33
|
+
async function runTool(call, signal) {
|
|
34
|
+
const base = {
|
|
35
|
+
role: "tool",
|
|
36
|
+
id: call.id,
|
|
37
|
+
toolName: call.toolName
|
|
38
|
+
};
|
|
39
|
+
const definition = Object.hasOwn(tools, call.toolName) ? tools[call.toolName] : void 0;
|
|
40
|
+
if (!definition) return {
|
|
41
|
+
...base,
|
|
42
|
+
error: `Unknown tool: ${call.toolName}`
|
|
43
|
+
};
|
|
44
|
+
const stopped = {
|
|
45
|
+
...base,
|
|
46
|
+
error: "Stopped"
|
|
47
|
+
};
|
|
48
|
+
try {
|
|
49
|
+
let args = call.args;
|
|
50
|
+
if (definition.schema) {
|
|
51
|
+
const outcome = await definition.schema["~standard"].validate(args);
|
|
52
|
+
if (signal.aborted) return stopped;
|
|
53
|
+
if (outcome.issues) return {
|
|
54
|
+
...base,
|
|
55
|
+
error: `Invalid arguments: ${issuesText(outcome.issues)}`
|
|
56
|
+
};
|
|
57
|
+
args = outcome.value;
|
|
58
|
+
}
|
|
59
|
+
if (definition.confirm) {
|
|
60
|
+
const answer = await confirmTool?.(call);
|
|
61
|
+
if (signal.aborted) return stopped;
|
|
62
|
+
if (!answer) return {
|
|
63
|
+
...base,
|
|
64
|
+
error: "The user declined to run this tool"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
...base,
|
|
69
|
+
result: await definition.handler(args)
|
|
70
|
+
};
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return {
|
|
73
|
+
...base,
|
|
74
|
+
error: toError(error).message
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function run(controller) {
|
|
79
|
+
const { signal } = controller;
|
|
80
|
+
const update = (patch) => {
|
|
81
|
+
if (!signal.aborted) store.setState(patch);
|
|
82
|
+
};
|
|
83
|
+
for (let round = 0;; round++) {
|
|
84
|
+
let content = "";
|
|
85
|
+
let index = -1;
|
|
86
|
+
const calls = [];
|
|
87
|
+
let iterator;
|
|
88
|
+
try {
|
|
89
|
+
iterator = transport.send(store.getSnapshot().messages, declarations, { signal })[Symbol.asyncIterator]();
|
|
90
|
+
for (;;) {
|
|
91
|
+
const step = await raceAbort(iterator.next(), signal);
|
|
92
|
+
if (!step || step.done || signal.aborted) break;
|
|
93
|
+
const chunk = step.value;
|
|
94
|
+
if (chunk.type === "done") break;
|
|
95
|
+
if (chunk.type === "tool-call") {
|
|
96
|
+
const { id, toolName, args } = chunk;
|
|
97
|
+
calls.push({
|
|
98
|
+
id,
|
|
99
|
+
toolName,
|
|
100
|
+
args
|
|
101
|
+
});
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
content += chunk.delta;
|
|
105
|
+
const text = content;
|
|
106
|
+
if (index < 0) {
|
|
107
|
+
index = store.getSnapshot().messages.length;
|
|
108
|
+
update((s) => ({ messages: [...s.messages, {
|
|
109
|
+
role: "assistant",
|
|
110
|
+
content: text
|
|
111
|
+
}] }));
|
|
112
|
+
} else update((s) => ({ messages: s.messages.map((m, i) => i === index ? {
|
|
113
|
+
role: "assistant",
|
|
114
|
+
content: text
|
|
115
|
+
} : m) }));
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (!signal.aborted) update(() => ({
|
|
119
|
+
status: "error",
|
|
120
|
+
error: toError(error)
|
|
121
|
+
}));
|
|
122
|
+
return;
|
|
123
|
+
} finally {
|
|
124
|
+
iterator?.return?.().catch(() => {});
|
|
125
|
+
}
|
|
126
|
+
if (signal.aborted) return;
|
|
127
|
+
if (calls.length === 0) break;
|
|
128
|
+
if (round >= maxToolRounds) {
|
|
129
|
+
update(() => ({
|
|
130
|
+
status: "error",
|
|
131
|
+
error: /* @__PURE__ */ new Error(`The assistant kept calling tools after ${maxToolRounds} rounds`)
|
|
132
|
+
}));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const results = [];
|
|
136
|
+
for (const call of calls) {
|
|
137
|
+
const result = await raceAbort(runTool(call, signal), signal);
|
|
138
|
+
if (!result) return;
|
|
139
|
+
results.push(result);
|
|
140
|
+
if (signal.aborted) return;
|
|
141
|
+
}
|
|
142
|
+
const assistant = {
|
|
143
|
+
role: "assistant",
|
|
144
|
+
content,
|
|
145
|
+
toolCalls: calls
|
|
146
|
+
};
|
|
147
|
+
update((s) => ({ messages: index < 0 ? [
|
|
148
|
+
...s.messages,
|
|
149
|
+
assistant,
|
|
150
|
+
...results
|
|
151
|
+
] : [...s.messages.map((m, i) => i === index ? assistant : m), ...results] }));
|
|
152
|
+
}
|
|
153
|
+
update(() => ({ status: "idle" }));
|
|
154
|
+
}
|
|
155
|
+
async function start(controller) {
|
|
156
|
+
active = controller;
|
|
157
|
+
try {
|
|
158
|
+
await run(controller);
|
|
159
|
+
} finally {
|
|
160
|
+
if (active === controller) active = null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
...store,
|
|
165
|
+
async send(text) {
|
|
166
|
+
if (destroyed) return;
|
|
167
|
+
if (!text.trim() || store.getSnapshot().status === "streaming") return;
|
|
168
|
+
store.setState((s) => ({
|
|
169
|
+
messages: [...s.messages, {
|
|
170
|
+
role: "user",
|
|
171
|
+
content: text
|
|
172
|
+
}],
|
|
173
|
+
status: "streaming",
|
|
174
|
+
error: null
|
|
175
|
+
}));
|
|
176
|
+
await start(new AbortController());
|
|
177
|
+
},
|
|
178
|
+
async retry() {
|
|
179
|
+
if (destroyed || store.getSnapshot().status !== "error") return;
|
|
180
|
+
const { messages } = store.getSnapshot();
|
|
181
|
+
let last = messages.length - 1;
|
|
182
|
+
while (last >= 0 && messages[last]?.role !== "user") last--;
|
|
183
|
+
if (last < 0) return;
|
|
184
|
+
store.setState({
|
|
185
|
+
messages: messages.slice(0, last + 1),
|
|
186
|
+
status: "streaming",
|
|
187
|
+
error: null
|
|
188
|
+
});
|
|
189
|
+
await start(new AbortController());
|
|
190
|
+
},
|
|
191
|
+
stop() {
|
|
192
|
+
if (!active) return;
|
|
193
|
+
active.abort();
|
|
194
|
+
active = null;
|
|
195
|
+
store.setState({ status: "idle" });
|
|
196
|
+
},
|
|
197
|
+
reset() {
|
|
198
|
+
active?.abort();
|
|
199
|
+
active = null;
|
|
200
|
+
store.setState({
|
|
201
|
+
messages: [],
|
|
202
|
+
status: "idle",
|
|
203
|
+
error: null
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
export { createAssistantSession };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AssistantTransport } from "./types.js";
|
|
2
|
+
export interface EchoAssistantTransportOptions {
|
|
3
|
+
/** Waits this long before each character. */
|
|
4
|
+
delayMs?: number;
|
|
5
|
+
}
|
|
6
|
+
/** Replies `Echo: <last user text>` one character at a time, with no model behind it. */
|
|
7
|
+
export declare function echoAssistantTransport({ delayMs, }?: EchoAssistantTransportOptions): AssistantTransport;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/assistant/echo-transport.ts
|
|
2
|
+
var wait = (ms, signal) => new Promise((resolve) => {
|
|
3
|
+
const timer = setTimeout(done, ms);
|
|
4
|
+
function done() {
|
|
5
|
+
clearTimeout(timer);
|
|
6
|
+
signal.removeEventListener("abort", done);
|
|
7
|
+
resolve();
|
|
8
|
+
}
|
|
9
|
+
signal.addEventListener("abort", done, { once: true });
|
|
10
|
+
});
|
|
11
|
+
/** Replies `Echo: <last user text>` one character at a time, with no model behind it. */
|
|
12
|
+
function echoAssistantTransport({ delayMs = 0 } = {}) {
|
|
13
|
+
return { async *send(messages, _tools, { signal }) {
|
|
14
|
+
let said = "";
|
|
15
|
+
for (const message of messages) if (message.role === "user") said = message.content;
|
|
16
|
+
const reply = `Echo: ${said}`;
|
|
17
|
+
for (const delta of reply) {
|
|
18
|
+
if (delayMs > 0) await wait(delayMs, signal);
|
|
19
|
+
if (signal.aborted) return;
|
|
20
|
+
yield {
|
|
21
|
+
type: "text",
|
|
22
|
+
delta
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
yield { type: "done" };
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
28
|
+
export { echoAssistantTransport };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AssistantTransport } from "./types.js";
|
|
2
|
+
export interface HttpAssistantTransportOptions {
|
|
3
|
+
url: string;
|
|
4
|
+
/** Resolved for each request so tokens can change between turns. */
|
|
5
|
+
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
6
|
+
credentials?: RequestCredentials;
|
|
7
|
+
fetch?: typeof globalThis.fetch;
|
|
8
|
+
}
|
|
9
|
+
/** Posts a conversation to an app endpoint that replies with NDJSON AssistantChunks. */
|
|
10
|
+
export declare function httpAssistantTransport(options: HttpAssistantTransportOptions): AssistantTransport;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { HttpError } from "../data/http-error.js";
|
|
2
|
+
import { isJsonObject, isRecord } from "../guards.js";
|
|
3
|
+
// src/assistant/http-transport.ts
|
|
4
|
+
function parseChunk(line, lineNumber) {
|
|
5
|
+
const invalid = () => /* @__PURE__ */ new Error(`Invalid assistant chunk at line ${lineNumber}`);
|
|
6
|
+
let chunk;
|
|
7
|
+
try {
|
|
8
|
+
chunk = JSON.parse(line);
|
|
9
|
+
} catch {
|
|
10
|
+
throw invalid();
|
|
11
|
+
}
|
|
12
|
+
if (isRecord(chunk)) switch (chunk.type) {
|
|
13
|
+
case "text":
|
|
14
|
+
if (typeof chunk.delta === "string") return {
|
|
15
|
+
type: "text",
|
|
16
|
+
delta: chunk.delta
|
|
17
|
+
};
|
|
18
|
+
break;
|
|
19
|
+
case "tool-call":
|
|
20
|
+
if (typeof chunk.id === "string" && typeof chunk.toolName === "string" && isJsonObject(chunk.args)) return {
|
|
21
|
+
type: "tool-call",
|
|
22
|
+
id: chunk.id,
|
|
23
|
+
toolName: chunk.toolName,
|
|
24
|
+
args: chunk.args
|
|
25
|
+
};
|
|
26
|
+
break;
|
|
27
|
+
case "done": return { type: "done" };
|
|
28
|
+
}
|
|
29
|
+
throw invalid();
|
|
30
|
+
}
|
|
31
|
+
/** Posts a conversation to an app endpoint that replies with NDJSON AssistantChunks. */
|
|
32
|
+
function httpAssistantTransport(options) {
|
|
33
|
+
const doFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
34
|
+
return { async *send(messages, tools, { signal }) {
|
|
35
|
+
try {
|
|
36
|
+
if (signal.aborted) return;
|
|
37
|
+
const headers = new Headers(typeof options.headers === "function" ? await options.headers() : options.headers);
|
|
38
|
+
if (signal.aborted) return;
|
|
39
|
+
headers.set("Content-Type", "application/json");
|
|
40
|
+
const response = await doFetch(options.url, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers,
|
|
43
|
+
credentials: options.credentials,
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
messages,
|
|
46
|
+
tools
|
|
47
|
+
}),
|
|
48
|
+
signal
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) throw new HttpError(`Assistant request failed (HTTP ${response.status})`, response.status, await response.text());
|
|
51
|
+
const reader = response.body?.getReader();
|
|
52
|
+
if (reader) {
|
|
53
|
+
const decoder = new TextDecoder();
|
|
54
|
+
let pending = "";
|
|
55
|
+
let lineNumber = 0;
|
|
56
|
+
let released = false;
|
|
57
|
+
const release = () => {
|
|
58
|
+
if (released) return;
|
|
59
|
+
released = true;
|
|
60
|
+
reader.cancel().catch(() => {});
|
|
61
|
+
reader.releaseLock();
|
|
62
|
+
};
|
|
63
|
+
signal.addEventListener("abort", release, { once: true });
|
|
64
|
+
try {
|
|
65
|
+
for (;;) {
|
|
66
|
+
if (signal.aborted) return;
|
|
67
|
+
const step = await reader.read();
|
|
68
|
+
if (signal.aborted) return;
|
|
69
|
+
pending += decoder.decode(step.value, { stream: !step.done });
|
|
70
|
+
if (step.done) pending += "\n";
|
|
71
|
+
let end = pending.indexOf("\n");
|
|
72
|
+
while (end >= 0) {
|
|
73
|
+
if (signal.aborted) return;
|
|
74
|
+
lineNumber++;
|
|
75
|
+
const line = pending.slice(0, end).trim();
|
|
76
|
+
pending = pending.slice(end + 1);
|
|
77
|
+
if (line) {
|
|
78
|
+
const chunk = parseChunk(line, lineNumber);
|
|
79
|
+
yield chunk;
|
|
80
|
+
if (chunk.type === "done") return;
|
|
81
|
+
}
|
|
82
|
+
end = pending.indexOf("\n");
|
|
83
|
+
}
|
|
84
|
+
if (step.done) break;
|
|
85
|
+
}
|
|
86
|
+
} finally {
|
|
87
|
+
signal.removeEventListener("abort", release);
|
|
88
|
+
release();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (signal.aborted) return;
|
|
92
|
+
yield { type: "done" };
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (!signal.aborted) throw error;
|
|
95
|
+
}
|
|
96
|
+
} };
|
|
97
|
+
}
|
|
98
|
+
export { httpAssistantTransport };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@tanstack/form-core";
|
|
2
|
+
import type { JsonObject, JsonValue } from "../guards.js";
|
|
3
|
+
/** A tool call the model asked for. `id` ties the result back to it. */
|
|
4
|
+
export interface AssistantToolCall {
|
|
5
|
+
id: string;
|
|
6
|
+
toolName: string;
|
|
7
|
+
args: JsonObject;
|
|
8
|
+
}
|
|
9
|
+
export type AssistantMessage = {
|
|
10
|
+
role: "user";
|
|
11
|
+
content: string;
|
|
12
|
+
} | {
|
|
13
|
+
role: "assistant";
|
|
14
|
+
content: string;
|
|
15
|
+
/** Present once the calls ran and their results follow as tool messages. */
|
|
16
|
+
toolCalls?: AssistantToolCall[];
|
|
17
|
+
} | {
|
|
18
|
+
role: "tool";
|
|
19
|
+
/** The `id` of the call this answers. */
|
|
20
|
+
id: string;
|
|
21
|
+
toolName: string;
|
|
22
|
+
/** What the handler returned; absent when `error` is set. */
|
|
23
|
+
result?: JsonValue;
|
|
24
|
+
error?: string;
|
|
25
|
+
};
|
|
26
|
+
export type AssistantChunk = {
|
|
27
|
+
type: "text";
|
|
28
|
+
delta: string;
|
|
29
|
+
} | ({
|
|
30
|
+
type: "tool-call";
|
|
31
|
+
} & AssistantToolCall) | {
|
|
32
|
+
type: "done";
|
|
33
|
+
};
|
|
34
|
+
export interface AssistantTool {
|
|
35
|
+
description: string;
|
|
36
|
+
/** A JSON Schema object, handed to the transport as is. */
|
|
37
|
+
parameters: JsonObject;
|
|
38
|
+
/** Method syntax keeps a handler with narrower `args` assignable. */
|
|
39
|
+
handler(args: JsonObject): JsonValue | undefined | Promise<JsonValue | undefined>;
|
|
40
|
+
/** Checks the arguments before `handler` runs; its output reaches `handler`. */
|
|
41
|
+
schema?: StandardSchemaV1<JsonObject, JsonObject>;
|
|
42
|
+
/** Ask the user through the session's `confirmTool` before this tool runs. */
|
|
43
|
+
confirm?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** What a transport learns about a tool: no handler, schema or confirm. */
|
|
46
|
+
export interface AssistantToolDeclaration {
|
|
47
|
+
description: string;
|
|
48
|
+
parameters: JsonObject;
|
|
49
|
+
}
|
|
50
|
+
export interface AssistantTransport {
|
|
51
|
+
send(messages: readonly AssistantMessage[], tools: Readonly<Record<string, AssistantToolDeclaration>>, options: {
|
|
52
|
+
signal: AbortSignal;
|
|
53
|
+
}): AsyncIterable<AssistantChunk>;
|
|
54
|
+
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import type { ResourceRecord } from "../resource/record.js";
|
|
1
2
|
import type { CanAccessParams } from "./types.js";
|
|
2
|
-
/**
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* One grant. `record` predicate (when present) must pass against a provided
|
|
5
|
+
* record, and reads it as `TRecord` — the same record shape the check is given.
|
|
6
|
+
*/
|
|
7
|
+
export interface Permission<TRecord extends ResourceRecord = ResourceRecord> {
|
|
4
8
|
action: string | string[];
|
|
5
9
|
resource: string;
|
|
6
|
-
record?: (record:
|
|
10
|
+
record?: (record: TRecord) => boolean;
|
|
7
11
|
}
|
|
8
|
-
/**
|
|
9
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Deny-by-default permission matcher (OR across permissions). `TRecord` is fixed
|
|
14
|
+
* by the permissions and the params together, so a grant's predicate and the
|
|
15
|
+
* record it reads are the same type.
|
|
16
|
+
*/
|
|
17
|
+
export declare function canAccessWithPermissions<TRecord extends ResourceRecord = ResourceRecord>(permissions: Permission<TRecord>[], params: CanAccessParams<TRecord>): boolean;
|
|
@@ -1,27 +1,31 @@
|
|
|
1
|
+
// src/auth/access-control.ts
|
|
1
2
|
function matchesAction(granted, action) {
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
const list = Array.isArray(granted) ? granted : [granted];
|
|
4
|
+
return list.includes("*") || list.includes(action);
|
|
4
5
|
}
|
|
5
6
|
function matchesResource(granted, resource) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
if (granted === "*") return true;
|
|
8
|
+
if (granted.endsWith(".*")) return resource.startsWith(granted.slice(0, -1));
|
|
9
|
+
return granted === resource;
|
|
9
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Deny-by-default permission matcher (OR across permissions). `TRecord` is fixed
|
|
13
|
+
* by the permissions and the params together, so a grant's predicate and the
|
|
14
|
+
* record it reads are the same type.
|
|
15
|
+
*/
|
|
10
16
|
function canAccessWithPermissions(permissions, params) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
return permissions.some((permission) => {
|
|
18
|
+
if (!matchesAction(permission.action, params.action)) return false;
|
|
19
|
+
if (!matchesResource(permission.resource, params.resource)) return false;
|
|
20
|
+
if (permission.record) {
|
|
21
|
+
if (params.record == null) return false;
|
|
22
|
+
try {
|
|
23
|
+
return permission.record(params.record);
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
24
30
|
}
|
|
25
|
-
export {
|
|
26
|
-
canAccessWithPermissions
|
|
27
|
-
};
|
|
31
|
+
export { canAccessWithPermissions };
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
+
import { type SkipToken } from "@tanstack/query-core";
|
|
1
2
|
import type { AuthProvider, CanAccessParams, UserIdentity } from "./types.js";
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
queryKey:
|
|
5
|
-
queryFn: () => Promise<
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
/** The option shape of the factories that skip with no provider. */
|
|
4
|
+
interface SkippableQueryOptions<TKey, TData> {
|
|
5
|
+
queryKey: TKey;
|
|
6
|
+
queryFn: SkipToken | (() => Promise<TData>);
|
|
7
|
+
}
|
|
8
|
+
/** What `getPermissions` takes: the key slot of {@link permissionsQueryOptions}. */
|
|
9
|
+
type PermissionsParams = Parameters<NonNullable<AuthProvider["getPermissions"]>>[0];
|
|
10
|
+
/**
|
|
11
|
+
* Plain option objects, same convention as the data factories (P2). With no
|
|
12
|
+
* provider, `identityQueryOptions`, `authStateQueryOptions` and
|
|
13
|
+
* `permissionsQueryOptions` set `queryFn: skipToken`, so the query never runs.
|
|
14
|
+
*/
|
|
15
|
+
export declare function identityQueryOptions(authProvider: AuthProvider | undefined): SkippableQueryOptions<readonly ["auth", "getIdentity"], UserIdentity>;
|
|
16
|
+
export declare function canAccessQueryOptions(authProvider: AuthProvider | undefined, params: CanAccessParams): {
|
|
17
|
+
queryKey: readonly ["auth", "canAccess", CanAccessParams<import("../index.js").ResourceRecord<import("../index.js").Identifier>>];
|
|
9
18
|
queryFn: () => Promise<boolean>;
|
|
10
19
|
};
|
|
11
|
-
export declare function authStateQueryOptions(authProvider: AuthProvider): {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
authenticated: boolean;
|
|
15
|
-
}>;
|
|
16
|
-
};
|
|
20
|
+
export declare function authStateQueryOptions(authProvider: AuthProvider | undefined): SkippableQueryOptions<readonly ["auth", "checkAuth"], {
|
|
21
|
+
authenticated: boolean;
|
|
22
|
+
}>;
|
|
17
23
|
/**
|
|
18
24
|
* Fetch the current user permissions via `authProvider.getPermissions`.
|
|
19
25
|
* No `getPermissions` on the provider = permissions not configured =
|
|
@@ -22,10 +28,7 @@ export declare function authStateQueryOptions(authProvider: AuthProvider): {
|
|
|
22
28
|
* a queryFn resolving `undefined`. Mirrors {@link canAccessQueryOptions}: keyed
|
|
23
29
|
* by the passed `params` so distinct param objects cache independently.
|
|
24
30
|
*/
|
|
25
|
-
export declare function permissionsQueryOptions(authProvider: AuthProvider, params?: unknown):
|
|
26
|
-
queryKey: readonly ["auth", "getPermissions", unknown];
|
|
27
|
-
queryFn: () => Promise<unknown>;
|
|
28
|
-
};
|
|
31
|
+
export declare function permissionsQueryOptions<TPermissions>(authProvider: AuthProvider<TPermissions> | undefined, params?: unknown): SkippableQueryOptions<readonly ["auth", "getPermissions", PermissionsParams], TPermissions | null>;
|
|
29
32
|
/**
|
|
30
33
|
* The triage result of {@link checkError}. Vanilla (react/vue-free) so both
|
|
31
34
|
* adapters share the reject-payload parsing before doing logout/notify/redirect.
|
|
@@ -55,3 +58,4 @@ export interface CheckErrorOutcome {
|
|
|
55
58
|
* redirect; only an auth rejection ever logs the user out.
|
|
56
59
|
*/
|
|
57
60
|
export declare function checkError(authProvider: AuthProvider, error: unknown): Promise<CheckErrorOutcome>;
|
|
61
|
+
export {};
|