@jxsuite/studio 8.1.0 → 9.0.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/chunks/{2020-dg097k3e.js → 2020-89fz3pfy.js} +3 -3
- package/dist/chunks/adapter-ay13gema.js +4 -0
- package/dist/chunks/add-new-flow-nmkqpjym.js +4 -0
- package/dist/chunks/cf-account-picker-2e06qxey.js +4 -0
- package/dist/chunks/cf-settings-5y04810c.js +4 -0
- package/dist/chunks/color-04361ttk.js +4 -0
- package/dist/chunks/{color-area-vh9cew51.js → color-area-h8dbasp1.js} +2 -2
- package/dist/chunks/{color-field-e78f16bs.js → color-field-qv5r2b16.js} +3 -3
- package/dist/chunks/{color-slider-fg4yhvk6.js → color-slider-nz2mrzky.js} +2 -2
- package/dist/chunks/components-3vf75n83.js +4 -0
- package/dist/chunks/content-source-4f2raf4a.js +4 -0
- package/dist/chunks/{convert-to-repeater-grnmea6c.js → convert-to-repeater-j9hfcj4f.js} +3 -3
- package/dist/chunks/editor-645krvtz.js +4 -0
- package/dist/chunks/entry-commands-3qmv8wkr.js +4 -0
- package/dist/chunks/{extension-sections-rp7fkega.js → extension-sections-thfke054.js} +3 -3
- package/dist/chunks/file-ops-kkhp9ma7.js +4 -0
- package/dist/chunks/files-yswdhxrf.js +4 -0
- package/dist/chunks/format-host-4g8ex6bz.js +4 -0
- package/dist/chunks/fs-events-z9gpvc6t.js +4 -0
- package/dist/chunks/grid-open-fm4j183c.js +4 -0
- package/dist/chunks/{grid-view-x26rfpz1.js → grid-view-7d2nx83y.js} +10 -10
- package/dist/chunks/{iframe-entry-pkgmcgp0.js → iframe-entry-2x9bn5ng.js} +4 -4
- package/dist/chunks/{iframe-entry-02wzcyvy.js → iframe-entry-btck0e9s.js} +4 -4
- package/dist/chunks/iframe-host-q7fedsmr.js +4 -0
- package/dist/chunks/{index-203h71wy.js → index-jxmt7vh8.js} +3 -3
- package/dist/chunks/index-pt4wh98x.js +4 -0
- package/dist/chunks/{javascript-ww5pv5ae.js → javascript-vmqp56ke.js} +2 -2
- package/dist/chunks/{jsonMode-vwbdwwq3.js → jsonMode-n3wmpant.js} +6 -6
- package/dist/chunks/jx-validate-19y62knt.js +4 -0
- package/dist/chunks/layers-panel-1qvc83y9.js +4 -0
- package/dist/chunks/layers-qf0bc7ze.js +4 -0
- package/dist/chunks/lit-html-wyqw04y2.js +4 -0
- package/dist/chunks/media-library-f9d5qcjb.js +4 -0
- package/dist/chunks/media-picker-rsb1fmk2.js +4 -0
- package/dist/chunks/{monaco-binding-63tqze27.js → monaco-binding-wfhmz89d.js} +3 -3
- package/dist/chunks/monaco-buffer-ak0raadw.js +4 -0
- package/dist/chunks/{monaco-setup-zyyynnj8.js → monaco-setup-fty7a8vg.js} +3 -3
- package/dist/chunks/notify-4qj70q0x.js +4 -0
- package/dist/chunks/parse-n8dfxnqf.js +4 -0
- package/dist/chunks/platform-ehg6t9vd.js +4 -0
- package/dist/chunks/publish-panel-yhmda3cd.js +4 -0
- package/dist/chunks/{publish-quick-ajzdhhs6.js → publish-quick-12hh8926.js} +3 -3
- package/dist/chunks/publish-workspace-hx8qdv9p.js +4 -0
- package/dist/chunks/quick-search-r955m3q1.js +4 -0
- package/dist/chunks/references-8gxv0x7t.js +4 -0
- package/dist/chunks/right-panel-jarwqjpy.js +4 -0
- package/dist/chunks/source-control-84qt25m9.js +4 -0
- package/dist/chunks/studio-0nefxhar.js +7 -0
- package/dist/chunks/studio-0rg30pvs.js +5 -0
- package/dist/chunks/{studio-3q6n9qvq.js → studio-14hh60j8.js} +4 -4
- package/dist/chunks/studio-1cfyd1ys.js +5 -0
- package/dist/chunks/studio-2bq7hheq.js +5 -0
- package/dist/chunks/{studio-mmqr9awv.js → studio-32d8wyxq.js} +33 -33
- package/dist/chunks/studio-3hpcawh8.js +4 -0
- package/dist/chunks/{studio-zm7dnr9g.js → studio-4j35hgsw.js} +4 -4
- package/dist/chunks/{studio-bqgs90r8.js → studio-4qg61g9j.js} +4 -4
- package/dist/chunks/studio-4rgem12q.js +5 -0
- package/dist/chunks/studio-6k9djnxw.js +5 -0
- package/dist/chunks/studio-6rbaedw2.js +9 -0
- package/dist/chunks/studio-732mw0g8.js +5 -0
- package/dist/chunks/studio-79r8msnr.js +7 -0
- package/dist/chunks/studio-7q6jdd4h.js +5 -0
- package/dist/chunks/{studio-jqrg9xxg.js → studio-881z1xrx.js} +4 -4
- package/dist/chunks/{studio-9wyqyfqy.js → studio-8hf8qcm1.js} +4 -4
- package/dist/chunks/{studio-6vrxzkba.js → studio-8pne0rx4.js} +4 -4
- package/dist/chunks/{studio-wcakp5bt.js → studio-9179bsm7.js} +7 -7
- package/dist/chunks/{studio-xddgbwhe.js → studio-9ff0vqff.js} +3 -3
- package/dist/chunks/{studio-25a5by3h.js → studio-9gzvypw4.js} +4 -4
- package/dist/chunks/studio-9ndgd55w.js +5 -0
- package/dist/chunks/studio-9ra9e7m4.js +5 -0
- package/dist/chunks/studio-a6e5fzve.js +5 -0
- package/dist/chunks/studio-a9k86nq6.js +5 -0
- package/dist/chunks/studio-ae7a69jc.js +6 -0
- package/dist/chunks/studio-aghsg0ds.js +7 -0
- package/dist/chunks/{studio-2b8443ms.js → studio-ajw4k8hp.js} +3 -3
- package/dist/chunks/{studio-957jg8g3.js → studio-apapy17g.js} +4 -4
- package/dist/chunks/{studio-hnx1h1pr.js → studio-by8x0jvc.js} +4 -4
- package/dist/chunks/{studio-hxq1ebp0.js → studio-c88gq88j.js} +6 -6
- package/dist/chunks/{studio-a6x058ew.js → studio-cg54vtet.js} +4 -4
- package/dist/chunks/studio-dvhxxxbd.js +5 -0
- package/dist/chunks/{studio-1meqbxns.js → studio-e4rq6x0q.js} +4 -4
- package/dist/chunks/{studio-v2ek69cb.js → studio-ekzbgnjp.js} +4 -4
- package/dist/chunks/{studio-2dtc90zs.js → studio-fg4fj9ec.js} +6 -6
- package/dist/chunks/studio-fj6jzgxw.js +5 -0
- package/dist/chunks/{studio-txkx6586.js → studio-g45r5vm8.js} +7 -7
- package/dist/chunks/{studio-v0gsjav0.js → studio-gczve8zf.js} +4 -4
- package/dist/chunks/studio-ge08zwpv.js +5 -0
- package/dist/chunks/{studio-1gdfsr49.js → studio-gk9yk8dj.js} +7 -7
- package/dist/chunks/{studio-cpdzrdgp.js → studio-gskkgfe3.js} +4 -4
- package/dist/chunks/{studio-94ppxeqy.js → studio-h4jbgne2.js} +8 -8
- package/dist/chunks/{studio-kdseew9m.js → studio-hcy9zeac.js} +4 -4
- package/dist/chunks/studio-ht81xjkr.js +5 -0
- package/dist/chunks/{studio-73wz63nn.js → studio-jr30g4ae.js} +4 -4
- package/dist/chunks/{studio-fqs1a22a.js → studio-k5n4es74.js} +4 -4
- package/dist/chunks/{studio-j5jqvyn3.js → studio-k6hbx2fc.js} +4 -4
- package/dist/chunks/{studio-332hesdk.js → studio-kqh7fxn6.js} +4 -4
- package/dist/chunks/{studio-8nyw10dp.js → studio-kyw47pet.js} +4 -4
- package/dist/chunks/studio-mkw17ngg.js +5 -0
- package/dist/chunks/{studio-02phtmqn.js → studio-myb4mf0h.js} +4 -4
- package/dist/chunks/{studio-d140sy8e.js → studio-n4qq1n57.js} +4 -4
- package/dist/chunks/{studio-bf0rgyf7.js → studio-nfmpmkqb.js} +4 -4
- package/dist/chunks/studio-nfwacw31.js +5 -0
- package/dist/chunks/{studio-z42kysg7.js → studio-nm86874b.js} +136 -136
- package/dist/chunks/studio-nqexty8x.js +16 -0
- package/dist/chunks/studio-nr0kcqc6.js +5 -0
- package/dist/chunks/{studio-dmn7gpba.js → studio-nvfa137z.js} +5 -5
- package/dist/chunks/studio-psh8mwnw.js +4 -0
- package/dist/chunks/{studio-4dsdnan2.js → studio-qd0cycm4.js} +5 -5
- package/dist/chunks/{studio-zht0x0v3.js → studio-qevf5z9d.js} +4 -4
- package/dist/chunks/studio-qs5fmhtj.js +14 -0
- package/dist/chunks/studio-ry4789ng.js +7 -0
- package/dist/chunks/studio-s4fq9jht.js +5 -0
- package/dist/chunks/studio-sc35ttdk.js +5 -0
- package/dist/chunks/studio-sm2f0sc6.js +6 -0
- package/dist/chunks/{studio-rdnezzkd.js → studio-spza69tb.js} +5 -5
- package/dist/chunks/studio-t6dqnnft.js +5 -0
- package/dist/chunks/{studio-hw60n5dh.js → studio-tfmhnwyr.js} +3 -3
- package/dist/chunks/studio-tn7v8gey.js +6 -0
- package/dist/chunks/{studio-nsv7g1qn.js → studio-trcfer2b.js} +4 -4
- package/dist/chunks/studio-tyw7tk0x.js +5 -0
- package/dist/chunks/{studio-533py3yy.js → studio-tyzgte21.js} +4 -4
- package/dist/chunks/studio-vtey9w98.js +5 -0
- package/dist/chunks/studio-ws2r1d7e.js +5 -0
- package/dist/chunks/studio-x2trkhk5.js +5 -0
- package/dist/chunks/{studio-c5dj3dzk.js → studio-xzgp0cp7.js} +4 -4
- package/dist/chunks/studio-y47090vq.js +5 -0
- package/dist/chunks/{studio-nm9qev4p.js → studio-yjh9gddd.js} +5 -5
- package/dist/chunks/studio-ywf9y1e2.js +5 -0
- package/dist/chunks/{studio-x3ge4rts.js → studio-z1p91z5a.js} +4 -4
- package/dist/chunks/studio-ztppbam0.js +5 -0
- package/dist/chunks/{toast-host-1yg5ar8d.js → toast-host-g3r2s0kn.js} +2 -2
- package/dist/chunks/toast-w11qpkyz.js +4 -0
- package/dist/chunks/{tsMode-vra2hh7y.js → tsMode-byje4a01.js} +5 -5
- package/dist/chunks/workspace-7q4arnt3.js +4 -0
- package/dist/chunks/workspace-home-ge027kr6.js +4 -0
- package/dist/chunks/{ws-client-72x5n99j.js → ws-client-h09h6sqg.js} +3 -3
- package/dist/iframe-entry.js +13 -13
- package/dist/manifest.json +77 -77
- package/dist/studio.js +89 -89
- package/package.json +11 -12
- package/src/collab/collab-session.ts +6 -2
- package/src/commands/context.ts +4 -0
- package/src/commands/defaults.ts +2 -0
- package/src/commands/live-context.ts +2 -0
- package/src/files/files.ts +6 -3
- package/src/new-project/new-project-modal.ts +1 -4
- package/src/panels/ai-panel.ts +27 -34
- package/src/platform.ts +20 -0
- package/src/platforms/devserver.ts +2 -120
- package/src/services/ai-ask.ts +29 -171
- package/src/services/ai-command-tools.ts +40 -50
- package/src/services/ai-import-tools.ts +4 -5
- package/src/services/ai-project-tools.ts +13 -3
- package/src/services/ai-tools.ts +26 -564
- package/src/services/ai-writes.ts +84 -2
- package/src/services/document-assistant.ts +29 -58
- package/src/services/harness/assistant-tools.ts +68 -0
- package/src/services/harness/interaction-store.ts +118 -0
- package/src/services/harness/studio-documents.ts +105 -0
- package/src/services/harness/turn-hooks.ts +1 -6
- package/src/services/project-adoption.ts +8 -16
- package/src/services/tool-executor.ts +25 -22
- package/src/state.ts +3 -13
- package/src/surfaces/files-panel.json +12 -0
- package/src/surfaces/files-panel.ts +5 -2
- package/src/surfaces/welcome.ts +4 -1
- package/src/tabs/selection.ts +14 -109
- package/src/tabs/tab.ts +10 -1
- package/src/tabs/transact.ts +316 -124
- package/src/types.ts +13 -8
- package/dist/chunks/adapter-tck81zft.js +0 -4
- package/dist/chunks/add-new-flow-a1w0bnm6.js +0 -4
- package/dist/chunks/cf-account-picker-ce3kp3x1.js +0 -4
- package/dist/chunks/cf-settings-7ye6x7g2.js +0 -4
- package/dist/chunks/color-4gj49xq6.js +0 -4
- package/dist/chunks/components-qce0v93f.js +0 -4
- package/dist/chunks/content-source-jjx8kgzr.js +0 -4
- package/dist/chunks/editor-bkja5dbv.js +0 -4
- package/dist/chunks/entry-commands-1wh0btvg.js +0 -4
- package/dist/chunks/file-ops-6pe4z7an.js +0 -4
- package/dist/chunks/files-ybpp3j38.js +0 -4
- package/dist/chunks/format-host-3mtbhxk1.js +0 -4
- package/dist/chunks/fs-events-xfanfxds.js +0 -4
- package/dist/chunks/grid-open-dg5qmnaj.js +0 -4
- package/dist/chunks/iframe-host-j2bk2xkc.js +0 -4
- package/dist/chunks/index-fkrm7pdk.js +0 -4
- package/dist/chunks/jx-validate-w7v32f6a.js +0 -4
- package/dist/chunks/layers-g7j732cm.js +0 -4
- package/dist/chunks/layers-panel-gjrsxcck.js +0 -4
- package/dist/chunks/lit-html-jdfz9rhc.js +0 -4
- package/dist/chunks/media-library-9j0v4wmk.js +0 -4
- package/dist/chunks/media-picker-cpyyt2m0.js +0 -4
- package/dist/chunks/monaco-buffer-9wcme176.js +0 -4
- package/dist/chunks/notify-rahnnnv4.js +0 -4
- package/dist/chunks/parse-v77xtwv7.js +0 -4
- package/dist/chunks/platform-b2aqv7v5.js +0 -4
- package/dist/chunks/publish-panel-4yk2461b.js +0 -4
- package/dist/chunks/publish-workspace-mxestzcf.js +0 -4
- package/dist/chunks/quick-search-9g6z65b0.js +0 -4
- package/dist/chunks/references-c26enpxs.js +0 -4
- package/dist/chunks/right-panel-9d0e0yg7.js +0 -4
- package/dist/chunks/source-control-xyn3a8ds.js +0 -4
- package/dist/chunks/studio-0khd0xdw.js +0 -5
- package/dist/chunks/studio-1fvzq9vs.js +0 -5
- package/dist/chunks/studio-1yhz0s8p.js +0 -5
- package/dist/chunks/studio-2c2mjyzj.js +0 -6
- package/dist/chunks/studio-3pndhat0.js +0 -9
- package/dist/chunks/studio-4fjqdzrb.js +0 -6
- package/dist/chunks/studio-5abge3wy.js +0 -5
- package/dist/chunks/studio-74nzv280.js +0 -5
- package/dist/chunks/studio-7cg75gtj.js +0 -14
- package/dist/chunks/studio-89cwmnae.js +0 -5
- package/dist/chunks/studio-8m1t83kn.js +0 -7
- package/dist/chunks/studio-a3evp7f6.js +0 -6
- package/dist/chunks/studio-ana67wax.js +0 -5
- package/dist/chunks/studio-b4xhpcwy.js +0 -4
- package/dist/chunks/studio-c4rcaftm.js +0 -5
- package/dist/chunks/studio-cgjt1zce.js +0 -5
- package/dist/chunks/studio-cq87hree.js +0 -5
- package/dist/chunks/studio-e1g64q43.js +0 -5
- package/dist/chunks/studio-e8pzjjhf.js +0 -5
- package/dist/chunks/studio-f8kz3krj.js +0 -7
- package/dist/chunks/studio-fw3jp9m7.js +0 -5
- package/dist/chunks/studio-g6ysm05p.js +0 -7
- package/dist/chunks/studio-gmhd0j3f.js +0 -5
- package/dist/chunks/studio-gqpty626.js +0 -5
- package/dist/chunks/studio-grp684wx.js +0 -5
- package/dist/chunks/studio-h98520qr.js +0 -5
- package/dist/chunks/studio-hvxt8z79.js +0 -5
- package/dist/chunks/studio-j4e627w1.js +0 -5
- package/dist/chunks/studio-kt2j51sa.js +0 -5
- package/dist/chunks/studio-m4w6sygj.js +0 -16
- package/dist/chunks/studio-nqxqf8p2.js +0 -4
- package/dist/chunks/studio-p89w12s2.js +0 -7
- package/dist/chunks/studio-pbagym4j.js +0 -5
- package/dist/chunks/studio-pr23qhrw.js +0 -5
- package/dist/chunks/studio-r4ya3sb2.js +0 -5
- package/dist/chunks/studio-rm9m3z60.js +0 -5
- package/dist/chunks/studio-tpmnbgmt.js +0 -5
- package/dist/chunks/studio-ttxhq5yt.js +0 -5
- package/dist/chunks/studio-y1x65h5r.js +0 -5
- package/dist/chunks/studio-zf188hnd.js +0 -5
- package/dist/chunks/toast-1gv0h53h.js +0 -4
- package/dist/chunks/workspace-3nt93k48.js +0 -4
- package/dist/chunks/workspace-home-zbgyqqws.js +0 -4
- package/src/services/directory-picker.ts +0 -187
- package/src/services/gated-registry.ts +0 -77
package/src/services/ai-ask.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ai-ask.ts — the assistant's one way to stop and put a question to the author.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `finishStream` has already set the state to `"idle"` by the time tools run, so a suspended turn
|
|
9
|
-
* is idle by construction and this store is what "waiting" means.
|
|
4
|
+
* `ask_user` declares what it asks (`interaction`) and what its answer means (`settle`), and has no
|
|
5
|
+
* body that waits: the invoker answers `needs_input` before any tool code runs, the turn engine
|
|
6
|
+
* puts the question to the assistant's interaction store (`harness/interaction-store.ts`), and the
|
|
7
|
+
* person's answer comes back through `settle` as the call's result (specs/ai.md §3.4 and §3.11).
|
|
10
8
|
*
|
|
11
9
|
* Two decisions are load-bearing and easy to get wrong on a rewrite:
|
|
12
10
|
*
|
|
@@ -17,29 +15,16 @@
|
|
|
17
15
|
* between them is a 400. The answer travels as the tool result, and the chat view renders it into
|
|
18
16
|
* the question's own chip.
|
|
19
17
|
*
|
|
20
|
-
* Exactly one question can be outstanding, because `runAgentLoop` executes a round's tool calls
|
|
21
|
-
* serially. The guard below is defensive, not load-bearing.
|
|
22
|
-
*
|
|
23
18
|
* @license MIT
|
|
24
19
|
*/
|
|
25
20
|
|
|
21
|
+
import { JX_TOOL_FACTS } from "@jxsuite/ai/jx-tools";
|
|
26
22
|
import { createToolDefinition, toolError, toolSuccess } from "@jxsuite/ai/tools";
|
|
27
23
|
import { isString } from "@jxsuite/schema/guards";
|
|
28
|
-
import { reactive } from "../reactivity";
|
|
29
|
-
|
|
30
|
-
import type { ToolRegistry } from "@jxsuite/ai/tools";
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
export interface PendingAsk {
|
|
34
|
-
id: string;
|
|
35
|
-
question: string;
|
|
36
|
-
/** Short choices rendered as buttons. Free text is always allowed alongside them. */
|
|
37
|
-
options: string[];
|
|
38
|
-
/** One line of why-I'm-asking, rendered under the question. */
|
|
39
|
-
context: string;
|
|
40
|
-
}
|
|
25
|
+
import type { InteractionResponse, ToolRegistry } from "@jxsuite/ai/tools";
|
|
41
26
|
|
|
42
|
-
/**
|
|
27
|
+
/** What a settled question's result carries: the answer, or `null` with `skipped` for "you decide". */
|
|
43
28
|
export interface AskAnswer {
|
|
44
29
|
answer: string | null;
|
|
45
30
|
skipped: boolean;
|
|
@@ -48,112 +33,6 @@ export interface AskAnswer {
|
|
|
48
33
|
/** Max choices rendered as buttons; beyond this the question wants free text, not a menu. */
|
|
49
34
|
const MAX_OPTIONS = 6;
|
|
50
35
|
|
|
51
|
-
const state = reactive<{ pending: PendingAsk | null }>({ pending: null });
|
|
52
|
-
|
|
53
|
-
/** The resolver for {@link state.pending}, kept out of the reactive object (it is not render data). */
|
|
54
|
-
let settle: ((answer: AskAnswer) => void) | null = null;
|
|
55
|
-
|
|
56
|
-
/** Torn down with the question so a stopped turn does not leave a listener on a dead signal. */
|
|
57
|
-
let detachAbort: (() => void) | null = null;
|
|
58
|
-
|
|
59
|
-
function clear() {
|
|
60
|
-
state.pending = null;
|
|
61
|
-
settle = null;
|
|
62
|
-
detachAbort?.();
|
|
63
|
-
detachAbort = null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* The outstanding question, or null. Reactive — read it inside an `effect()` to repaint on it.
|
|
68
|
-
*
|
|
69
|
-
* @returns {PendingAsk | null}
|
|
70
|
-
*/
|
|
71
|
-
export function pendingAsk(): PendingAsk | null {
|
|
72
|
-
return state.pending;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Whether a turn is suspended on the author. The composer's Send becomes Answer on this. */
|
|
76
|
-
export function isAwaitingAnswer(): boolean {
|
|
77
|
-
return state.pending !== null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Register a question and suspend until it is settled.
|
|
82
|
-
*
|
|
83
|
-
* @param {PendingAsk} ask
|
|
84
|
-
* @param {AbortSignal} [signal] - The asking call's signal. Without one the question can still be
|
|
85
|
-
* settled by {@link cancelAsk}, but a Stop would leave the turn awaiting a promise nothing could
|
|
86
|
-
* resolve.
|
|
87
|
-
* @returns {Promise<AskAnswer>} Resolves on an answer, a skip, or the turn being stopped (`{
|
|
88
|
-
* answer: null, skipped: false }` — the shape the tool reports as a failure).
|
|
89
|
-
*/
|
|
90
|
-
export function askUser(ask: PendingAsk, signal?: AbortSignal): Promise<AskAnswer> {
|
|
91
|
-
return new Promise<AskAnswer>((resolve) => {
|
|
92
|
-
state.pending = ask;
|
|
93
|
-
settle = resolve;
|
|
94
|
-
|
|
95
|
-
if (signal?.aborted) {
|
|
96
|
-
clear();
|
|
97
|
-
resolve({ answer: null, skipped: false });
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (signal) {
|
|
101
|
-
const onAbort = () => {
|
|
102
|
-
cancelAsk();
|
|
103
|
-
};
|
|
104
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
105
|
-
detachAbort = () => signal.removeEventListener("abort", onAbort);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Answer the outstanding question.
|
|
112
|
-
*
|
|
113
|
-
* @param {string} text
|
|
114
|
-
* @returns {boolean} False when nothing was pending, so a caller can fall through to a normal send.
|
|
115
|
-
*/
|
|
116
|
-
export function answerAsk(text: string): boolean {
|
|
117
|
-
if (!settle) {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
const resolve = settle;
|
|
121
|
-
clear();
|
|
122
|
-
resolve({ answer: text, skipped: false });
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Decline to answer — "you decide". Settles as a success, so the turn carries on.
|
|
128
|
-
*
|
|
129
|
-
* @returns {boolean} False when nothing was pending.
|
|
130
|
-
*/
|
|
131
|
-
export function skipAsk(): boolean {
|
|
132
|
-
if (!settle) {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
const resolve = settle;
|
|
136
|
-
clear();
|
|
137
|
-
resolve({ answer: null, skipped: true });
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Settle an outstanding question as unanswered. Called from `stop()` and from the turn's abort. */
|
|
142
|
-
export function cancelAsk(): void {
|
|
143
|
-
if (!settle) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const resolve = settle;
|
|
147
|
-
clear();
|
|
148
|
-
resolve({ answer: null, skipped: false });
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Drop any outstanding question without settling a turn. For New Chat and for tests. */
|
|
152
|
-
export function resetAsk(): void {
|
|
153
|
-
cancelAsk();
|
|
154
|
-
clear();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
36
|
/**
|
|
158
37
|
* Register `ask_user` into a tool registry.
|
|
159
38
|
*
|
|
@@ -163,24 +42,12 @@ export function resetAsk(): void {
|
|
|
163
42
|
*
|
|
164
43
|
* @param {Pick<ToolRegistry, "register">} registry
|
|
165
44
|
*/
|
|
166
|
-
|
|
167
|
-
export interface AskToolOptions {
|
|
168
|
-
/**
|
|
169
|
-
* Called once a question has been put to the author, before the turn waits on the answer. A host
|
|
170
|
-
* saves the conversation here: a turn suspended on a person may wait for as long as they like,
|
|
171
|
-
* and a reload in the meantime must still find the question to restore as open (`specs/ai.md`
|
|
172
|
-
* §3.4).
|
|
173
|
-
*/
|
|
174
|
-
onPending?: () => void;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export function registerAskTool(
|
|
178
|
-
registry: Pick<ToolRegistry, "register">,
|
|
179
|
-
{ onPending }: AskToolOptions = {},
|
|
180
|
-
): void {
|
|
45
|
+
export function registerAskTool(registry: Pick<ToolRegistry, "register">): void {
|
|
181
46
|
registry.register(
|
|
182
47
|
createToolDefinition({
|
|
183
48
|
name: "ask_user",
|
|
49
|
+
title: "Ask the user",
|
|
50
|
+
annotations: JX_TOOL_FACTS.ask_user.annotations,
|
|
184
51
|
// It suspends the turn on the author, so a round that only asked spends no work budget.
|
|
185
52
|
interactive: true,
|
|
186
53
|
description:
|
|
@@ -212,43 +79,32 @@ export function registerAskTool(
|
|
|
212
79
|
},
|
|
213
80
|
required: ["question"],
|
|
214
81
|
},
|
|
215
|
-
|
|
82
|
+
interaction(args) {
|
|
216
83
|
/* Every field is any JSON value, narrowed by a guard rather than by `?.`. The registry's
|
|
217
|
-
`validate()` is "a lightweight structural check, not a JSON Schema validator",
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
84
|
+
`validate()` is "a lightweight structural check, not a JSON Schema validator", and this
|
|
85
|
+
tool may be registered into someone else's registry, so an argument that is a number is
|
|
86
|
+
a case, not an impossibility. Optional chaining guards null, not type: `context?.trim()`
|
|
87
|
+
threw on one. */
|
|
221
88
|
const { question, options, context } = args;
|
|
222
89
|
if (!isString(question) || !question.trim()) {
|
|
223
|
-
return
|
|
224
|
-
}
|
|
225
|
-
if (isAwaitingAnswer()) {
|
|
226
|
-
return toolError("A question is already waiting for the user.");
|
|
90
|
+
return { refusal: 'Pass "question" — the question to put to the user.' };
|
|
227
91
|
}
|
|
228
92
|
const choices = Array.isArray(options)
|
|
229
93
|
? options
|
|
230
94
|
.filter((o): o is string => isString(o) && o.trim().length > 0)
|
|
231
95
|
.slice(0, MAX_OPTIONS)
|
|
232
96
|
: [];
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
ctx.signal,
|
|
245
|
-
);
|
|
246
|
-
// The question is published by now (`askUser` sets it synchronously); the answer is not.
|
|
247
|
-
if (isAwaitingAnswer()) {
|
|
248
|
-
onPending?.();
|
|
249
|
-
}
|
|
250
|
-
const { answer, skipped } = await answering;
|
|
251
|
-
|
|
97
|
+
return {
|
|
98
|
+
context: isString(context) ? context.trim() : "",
|
|
99
|
+
kind: "question",
|
|
100
|
+
options: choices,
|
|
101
|
+
question: question.trim(),
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
settle(_args, response) {
|
|
105
|
+
/* SAFETY: `ask_user` only ever asks a question, so the response the invoker hands back is
|
|
106
|
+
the answer to one. */
|
|
107
|
+
const { answer, skipped } = response as Extract<InteractionResponse, { kind: "question" }>;
|
|
252
108
|
if (skipped) {
|
|
253
109
|
/* A success: the model asked, and "you decide" is an answer. Rendering it as a failure
|
|
254
110
|
would have the model apologise for having asked a fair question. */
|
|
@@ -262,6 +118,8 @@ export function registerAskTool(
|
|
|
262
118
|
}
|
|
263
119
|
return toolSuccess({ answer, skipped: false }, `The user answered: "${answer}"`);
|
|
264
120
|
},
|
|
121
|
+
// Never reached through the invoker, which settles an interaction instead of executing it.
|
|
122
|
+
execute: () => toolError("ask_user needs a person, and none is available here."),
|
|
265
123
|
}),
|
|
266
124
|
);
|
|
267
125
|
}
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
* @license MIT
|
|
61
61
|
*/
|
|
62
62
|
|
|
63
|
+
import { toolFacts } from "@jxsuite/ai/jx-tools";
|
|
63
64
|
import { createToolContext, toolError } from "@jxsuite/ai/tools";
|
|
64
65
|
import type {
|
|
65
66
|
ToolArguments,
|
|
@@ -82,6 +83,7 @@ import type { WriteReportDeps, WriteSnapshot } from "./ai-write-report";
|
|
|
82
83
|
import type { CommandContext } from "../commands/context";
|
|
83
84
|
import type { AiToolReport, AnyCommand, CommandRegistry } from "../commands/registry";
|
|
84
85
|
import type { Tab } from "../tabs/tab";
|
|
86
|
+
import { runAsActor } from "../tabs/transact";
|
|
85
87
|
|
|
86
88
|
/**
|
|
87
89
|
* The record a selection-level projection composes through.
|
|
@@ -214,8 +216,9 @@ function definitionOf(
|
|
|
214
216
|
} else {
|
|
215
217
|
parameters = command.args ?? EMPTY_ARGS;
|
|
216
218
|
}
|
|
217
|
-
|
|
219
|
+
const definition: ToolDefinition = {
|
|
218
220
|
name: tool.name,
|
|
221
|
+
title: command.title,
|
|
219
222
|
description: `${tool.description}${undoSuffix(command)}`,
|
|
220
223
|
/* SAFETY: `parameters` is an `argsSchema()` object (or the selector's composed from one), a
|
|
221
224
|
JSON Schema object whose only departures from `JSONSchema` are readonly arrays and a
|
|
@@ -227,6 +230,13 @@ function definitionOf(
|
|
|
227
230
|
llmStrict: false,
|
|
228
231
|
execute: (args, ctx) => execute(tool.name, args, ctx),
|
|
229
232
|
};
|
|
233
|
+
/* The facts are the table's, by tool name (`@jxsuite/ai/jx-tools`). A record with none projects a
|
|
234
|
+
tool without them, which the catalog refuses: that is the test that keeps the table whole. */
|
|
235
|
+
const facts = toolFacts(tool.name);
|
|
236
|
+
if (facts) {
|
|
237
|
+
definition.annotations = facts.annotations;
|
|
238
|
+
}
|
|
239
|
+
return definition;
|
|
230
240
|
}
|
|
231
241
|
|
|
232
242
|
/**
|
|
@@ -347,13 +357,21 @@ function fileLedger(
|
|
|
347
357
|
}
|
|
348
358
|
}
|
|
349
359
|
|
|
360
|
+
/** The command view, which also answers what the catalog asks of it. */
|
|
361
|
+
export interface CommandToolRegistry extends ToolRegistry {
|
|
362
|
+
/** Every record's projection, offered or not: the catalog's members from this side. */
|
|
363
|
+
readonly members: () => ToolDefinition[];
|
|
364
|
+
/** Whether the record projecting `name` is offered this round (its gate, and a non-empty enum). */
|
|
365
|
+
readonly advertises: (name: string) => boolean;
|
|
366
|
+
}
|
|
367
|
+
|
|
350
368
|
/**
|
|
351
369
|
* A ToolRegistry whose tools are the active registry's `aiTool` records. Nothing is registered.
|
|
352
370
|
*
|
|
353
371
|
* @param {CommandToolDeps} deps
|
|
354
|
-
* @returns {
|
|
372
|
+
* @returns {CommandToolRegistry}
|
|
355
373
|
*/
|
|
356
|
-
export function createCommandToolRegistry(deps: CommandToolDeps):
|
|
374
|
+
export function createCommandToolRegistry(deps: CommandToolDeps): CommandToolRegistry {
|
|
357
375
|
/** The record projecting `name`, enabled or not — so `execute` can reach a refusal. */
|
|
358
376
|
function commandFor(name: string): AnyCommand | undefined {
|
|
359
377
|
return activeRegistry()
|
|
@@ -408,10 +426,10 @@ export function createCommandToolRegistry(deps: CommandToolDeps): ToolRegistry {
|
|
|
408
426
|
const { paths, ...rest } = args;
|
|
409
427
|
await registry.run(SELECTOR, { paths });
|
|
410
428
|
before = registry.context();
|
|
411
|
-
await registry.run(command.id, rest);
|
|
429
|
+
await runAsActor(ctx.actor, () => registry.run(command.id, rest));
|
|
412
430
|
} else {
|
|
413
431
|
before = registry.context();
|
|
414
|
-
await registry.run(command.id, args);
|
|
432
|
+
await runAsActor(ctx.actor, () => registry.run(command.id, args));
|
|
415
433
|
}
|
|
416
434
|
} catch (error) {
|
|
417
435
|
return { error: error instanceof Error ? error.message : String(error), success: false };
|
|
@@ -507,52 +525,24 @@ export function createCommandToolRegistry(deps: CommandToolDeps): ToolRegistry {
|
|
|
507
525
|
return { valid: true };
|
|
508
526
|
},
|
|
509
527
|
execute,
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
* @param {ToolRegistry} hand
|
|
524
|
-
* @param {ToolRegistry} commands
|
|
525
|
-
* @returns {ToolRegistry}
|
|
526
|
-
*/
|
|
527
|
-
export function composeToolRegistries(hand: ToolRegistry, commands: ToolRegistry): ToolRegistry {
|
|
528
|
-
function sideOf(name: string): ToolRegistry | undefined {
|
|
529
|
-
if (hand.getDefinition(name)) {
|
|
530
|
-
return hand;
|
|
531
|
-
}
|
|
532
|
-
return commands.getDefinition(name) ? commands : undefined;
|
|
533
|
-
}
|
|
534
|
-
return {
|
|
535
|
-
register(tool) {
|
|
536
|
-
hand.register(tool);
|
|
537
|
-
},
|
|
538
|
-
list() {
|
|
539
|
-
return [...hand.list(), ...commands.list()];
|
|
540
|
-
},
|
|
541
|
-
listForLLM() {
|
|
542
|
-
return [...hand.listForLLM(), ...commands.listForLLM()];
|
|
543
|
-
},
|
|
544
|
-
getDefinition(name) {
|
|
545
|
-
return sideOf(name)?.getDefinition(name);
|
|
546
|
-
},
|
|
547
|
-
validate(name, args) {
|
|
548
|
-
const side = sideOf(name);
|
|
549
|
-
return side
|
|
550
|
-
? side.validate(name, args)
|
|
551
|
-
: { valid: false, errors: [`Unknown tool: "${name}"`] };
|
|
528
|
+
members() {
|
|
529
|
+
const registry = activeRegistry();
|
|
530
|
+
if (!registry) {
|
|
531
|
+
return [];
|
|
532
|
+
}
|
|
533
|
+
const definitions: ToolDefinition[] = [];
|
|
534
|
+
for (const command of projected(registry)) {
|
|
535
|
+
const definition = definitionOf(registry, command, execute);
|
|
536
|
+
if (definition) {
|
|
537
|
+
definitions.push(definition);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return definitions;
|
|
552
541
|
},
|
|
553
|
-
|
|
554
|
-
const
|
|
555
|
-
|
|
542
|
+
advertises(name) {
|
|
543
|
+
const registry = activeRegistry();
|
|
544
|
+
const command = commandFor(name);
|
|
545
|
+
return registry !== null && command !== undefined && advertised(registry, command);
|
|
556
546
|
},
|
|
557
547
|
};
|
|
558
548
|
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* @license MIT
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import { JX_TOOL_FACTS } from "@jxsuite/ai/jx-tools";
|
|
24
25
|
import { createToolDefinition, toolError, toolSuccess } from "@jxsuite/ai/tools";
|
|
25
26
|
import { IMPORT_MAX_PAGES } from "@jxsuite/protocol";
|
|
26
27
|
import { isBoolean, isNumber, isString } from "@jxsuite/schema/guards";
|
|
@@ -35,7 +36,6 @@ import { adoptCreatedProject } from "./project-adoption";
|
|
|
35
36
|
import type { AdoptCreatedOptions, AdoptOutcome } from "./project-adoption";
|
|
36
37
|
|
|
37
38
|
import type { JsonValue, ToolRegistry } from "@jxsuite/ai/tools";
|
|
38
|
-
import type { Tab } from "../tabs/tab";
|
|
39
39
|
import type {
|
|
40
40
|
ImportBreakpointPolicy,
|
|
41
41
|
ImportReadyEvent,
|
|
@@ -62,8 +62,6 @@ const DEFAULT_MIN_FIDELITY = 25;
|
|
|
62
62
|
const MAX_FIDELITY = 100;
|
|
63
63
|
|
|
64
64
|
export interface ImportToolsCtx {
|
|
65
|
-
/** The active tab, read AFTER adoption to re-anchor the agent loop's undo batch. */
|
|
66
|
-
getTab: () => Tab | null;
|
|
67
65
|
/** Open the imported project in this window. Defaults to the late-bound adopter. */
|
|
68
66
|
adoptProject?: (root: string) => Promise<void>;
|
|
69
67
|
/** Fired once adoption is VERIFIED — the session store re-keys the live chat here. */
|
|
@@ -269,7 +267,6 @@ function describeRun(id: string, root: string, summary?: ImportSiteSummary): str
|
|
|
269
267
|
export function registerImportTools(
|
|
270
268
|
registry: Pick<ToolRegistry, "register">,
|
|
271
269
|
{
|
|
272
|
-
getTab,
|
|
273
270
|
adoptProject,
|
|
274
271
|
onProjectAdopted,
|
|
275
272
|
brief: briefStore = { clear: clearPendingImportBrief, read: pendingImportBrief },
|
|
@@ -278,6 +275,8 @@ export function registerImportTools(
|
|
|
278
275
|
registry.register(
|
|
279
276
|
createToolDefinition({
|
|
280
277
|
name: "import_site",
|
|
278
|
+
title: "Import a site",
|
|
279
|
+
annotations: JX_TOOL_FACTS.import_site.annotations,
|
|
281
280
|
description:
|
|
282
281
|
"Clone a live website into a new Jx project and open it in the studio: pages, styles, " +
|
|
283
282
|
"assets, a shared layout and recurring components. Only available while no project is " +
|
|
@@ -454,7 +453,7 @@ export function registerImportTools(
|
|
|
454
453
|
* failure here is not the run's failure: the import continues either way, and the terminal
|
|
455
454
|
* path below reports honestly on whichever state the window ended up in.
|
|
456
455
|
*/
|
|
457
|
-
const adoptOptions: AdoptCreatedOptions = {
|
|
456
|
+
const adoptOptions: AdoptCreatedOptions = {};
|
|
458
457
|
if (adoptProject) {
|
|
459
458
|
adoptOptions.adopt = adoptProject;
|
|
460
459
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* @license MIT
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { JX_TOOL_FACTS } from "@jxsuite/ai/jx-tools";
|
|
17
18
|
import { createToolDefinition } from "@jxsuite/ai/tools";
|
|
18
19
|
import { isJsonObject, isNumber } from "@jxsuite/schema/guards";
|
|
19
20
|
import type { CreateProjectDestination, StudioPlatform } from "../types";
|
|
@@ -64,7 +65,6 @@ interface ListedEntry {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
export interface ProjectToolsCtx {
|
|
67
|
-
getTab: () => Tab | null;
|
|
68
68
|
validate?: (doc: CandidateDocument) => Promise<string[]>;
|
|
69
69
|
/**
|
|
70
70
|
* Gate for `project.json` writes — the per-project entry document, not the document schema. It
|
|
@@ -113,7 +113,6 @@ export interface ProjectToolsCtx {
|
|
|
113
113
|
export function registerProjectTools(
|
|
114
114
|
registry: Pick<ToolRegistry, "register">,
|
|
115
115
|
{
|
|
116
|
-
getTab,
|
|
117
116
|
validate = validateDoc,
|
|
118
117
|
validateProject = validateProjectConfig,
|
|
119
118
|
renderCheck,
|
|
@@ -130,6 +129,8 @@ export function registerProjectTools(
|
|
|
130
129
|
registry.register(
|
|
131
130
|
createToolDefinition({
|
|
132
131
|
name: "list_files",
|
|
132
|
+
title: "List files",
|
|
133
|
+
annotations: JX_TOOL_FACTS.list_files.annotations,
|
|
133
134
|
description:
|
|
134
135
|
"List the project's files recursively (paths relative to the project root, with type and " +
|
|
135
136
|
"size). Use this to discover pages, layouts, components, content, data, and styles before " +
|
|
@@ -199,6 +200,8 @@ export function registerProjectTools(
|
|
|
199
200
|
registry.register(
|
|
200
201
|
createToolDefinition({
|
|
201
202
|
name: "read_file",
|
|
203
|
+
title: "Read a file",
|
|
204
|
+
annotations: JX_TOOL_FACTS.read_file.annotations,
|
|
202
205
|
description:
|
|
203
206
|
"Read a project file's content by its project-relative path. Works for any text file " +
|
|
204
207
|
"(Jx documents, markdown, CSS, data). Large files are truncated.",
|
|
@@ -247,6 +250,8 @@ export function registerProjectTools(
|
|
|
247
250
|
registry.register(
|
|
248
251
|
createToolDefinition({
|
|
249
252
|
name: "write_file",
|
|
253
|
+
title: "Write a file",
|
|
254
|
+
annotations: JX_TOOL_FACTS.write_file.annotations,
|
|
250
255
|
description:
|
|
251
256
|
"Write a project file (create or overwrite) at a project-relative path. Jx documents " +
|
|
252
257
|
"(.json under pages/, layouts/, components/) are schema-validated and render-checked " +
|
|
@@ -422,6 +427,8 @@ export function registerProjectTools(
|
|
|
422
427
|
registry.register(
|
|
423
428
|
createToolDefinition({
|
|
424
429
|
name: "search_files",
|
|
430
|
+
title: "Search files",
|
|
431
|
+
annotations: JX_TOOL_FACTS.search_files.annotations,
|
|
425
432
|
description:
|
|
426
433
|
"Search project files by file NAME (not content). Returns matching paths. Use list_files " +
|
|
427
434
|
"for a directory overview and read_file to inspect contents.",
|
|
@@ -465,6 +472,8 @@ export function registerProjectTools(
|
|
|
465
472
|
registry.register(
|
|
466
473
|
createToolDefinition({
|
|
467
474
|
name: "create_project",
|
|
475
|
+
title: "Create a project",
|
|
476
|
+
annotations: JX_TOOL_FACTS.create_project.annotations,
|
|
468
477
|
description:
|
|
469
478
|
"Create a new Jx project (project.json, conventional directories, starter pages) and open " +
|
|
470
479
|
"it in the studio. Only available while no project is open. After it succeeds, the file " +
|
|
@@ -625,7 +634,6 @@ export function registerProjectTools(
|
|
|
625
634
|
*/
|
|
626
635
|
const { adopted, error: adoptionError } = await adoptCreatedProject(result.root, {
|
|
627
636
|
adopt: adoptProject,
|
|
628
|
-
getTab,
|
|
629
637
|
});
|
|
630
638
|
if (adopted) {
|
|
631
639
|
onProjectAdopted?.(result.root);
|
|
@@ -653,6 +661,8 @@ export function registerProjectTools(
|
|
|
653
661
|
registry.register(
|
|
654
662
|
createToolDefinition({
|
|
655
663
|
name: "list_starters",
|
|
664
|
+
title: "List starters",
|
|
665
|
+
annotations: JX_TOOL_FACTS.list_starters.annotations,
|
|
656
666
|
description:
|
|
657
667
|
"List the starter templates available for new projects (id, name, description). " +
|
|
658
668
|
"Currently informational — create_project scaffolds from built-in template variants.",
|