@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
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
import { initProjectRepo } from "../files/files";
|
|
13
13
|
import { getPlatform, hasPlatform } from "../platform";
|
|
14
|
-
import {
|
|
14
|
+
import { flushOpenBatches } from "../tabs/transact";
|
|
15
15
|
import { workspace } from "../workspace/workspace";
|
|
16
|
-
import type { Tab } from "../tabs/tab";
|
|
17
16
|
|
|
18
17
|
let _adopter: ((root: string) => Promise<void>) | null = null;
|
|
19
18
|
|
|
@@ -52,8 +51,6 @@ export interface AdoptOutcome {
|
|
|
52
51
|
|
|
53
52
|
/** The seams {@link adoptCreatedProject} runs through, so each caller keeps the one it already had. */
|
|
54
53
|
export interface AdoptCreatedOptions {
|
|
55
|
-
/** The active tab, read AFTER adoption to re-anchor the agent loop's undo batch. */
|
|
56
|
-
getTab: () => Tab | null;
|
|
57
54
|
/** The project-open flow. Defaults to this module's late-bound {@link adoptProject}. */
|
|
58
55
|
adopt?: (root: string) => Promise<void>;
|
|
59
56
|
/** Version-control init. Defaults to `files/files.ts`'s `initProjectRepo`. */
|
|
@@ -76,9 +73,8 @@ export interface AdoptCreatedOptions {
|
|
|
76
73
|
* - **Verifying.** `openRecentProject` swallows its failures into a status message, so a resolved
|
|
77
74
|
* promise is not proof of adoption. The caller must be told which of the two happened.
|
|
78
75
|
*
|
|
79
|
-
* The
|
|
80
|
-
* adoption replaces every tab in the workspace
|
|
81
|
-
* adoption left active.
|
|
76
|
+
* The third is the undo batch: the agent loop's lane may hold segments on pre-adoption tabs, and
|
|
77
|
+
* adoption replaces every tab in the workspace, so they are closed first (specs/ai.md §3.3).
|
|
82
78
|
*
|
|
83
79
|
* @param {string} root - Absolute root of the project that was just created.
|
|
84
80
|
* @param {AdoptCreatedOptions} opts
|
|
@@ -86,7 +82,7 @@ export interface AdoptCreatedOptions {
|
|
|
86
82
|
*/
|
|
87
83
|
export async function adoptCreatedProject(
|
|
88
84
|
root: string,
|
|
89
|
-
{
|
|
85
|
+
{ adopt = adoptProject, initRepo = initProjectRepo }: AdoptCreatedOptions = {},
|
|
90
86
|
): Promise<AdoptOutcome> {
|
|
91
87
|
// Never fails the create: a project that was written stays written (initProjectRepo notifies).
|
|
92
88
|
await initRepo(root);
|
|
@@ -117,19 +113,15 @@ export async function adoptCreatedProject(
|
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
/* The tabs an open lane has segments on are about to be replaced: close each now, against the
|
|
117
|
+
document it was written into. The lane stays open, so the turn's next write opens a segment on
|
|
118
|
+
whatever document it writes in the adopted project. */
|
|
119
|
+
flushOpenBatches();
|
|
124
120
|
let adoptionError: string | null = null;
|
|
125
121
|
try {
|
|
126
122
|
await adopt(root);
|
|
127
123
|
} catch (error) {
|
|
128
124
|
adoptionError = error instanceof Error ? error.message : String(error);
|
|
129
125
|
}
|
|
130
|
-
if (wasBatching) {
|
|
131
|
-
beginBatch(getTab());
|
|
132
|
-
}
|
|
133
|
-
|
|
134
126
|
return { adopted: workspace.projectRoot === root, error: adoptionError, openedElsewhere };
|
|
135
127
|
}
|
|
@@ -9,17 +9,19 @@
|
|
|
9
9
|
* happen (`services/harness/chat-reducer.ts`), and each round's request is projected from it, as
|
|
10
10
|
* it always was, so the provider sees the conversation the panel shows, with whatever the send
|
|
11
11
|
* path trimmed or sealed before the turn began.
|
|
12
|
-
* - **
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* - **The batch belongs to the assistant** (§3.3). The turn opens a batch lane owned by its actor,
|
|
13
|
+
* and every write the turn's tools make joins it, one segment per document written: each document
|
|
14
|
+
* gets one history step and one collab publish, whichever tab is active, and a person's edit
|
|
15
|
+
* during the turn closes the segment on that document rather than being folded into it.
|
|
15
16
|
* - **The ledger is filed under the last message the turn drew** (§3.2), in this module's `finally`,
|
|
16
17
|
* so the transcript draws the turn's changed-files summary under it.
|
|
17
18
|
*
|
|
18
19
|
* @license MIT
|
|
19
20
|
*/
|
|
20
21
|
|
|
22
|
+
import type { InteractionStore } from "./harness/interaction-store";
|
|
21
23
|
import type { createChatState } from "@jxsuite/ai/chat-state";
|
|
22
|
-
import { DEFAULT_TURN_POLICY, runTurn } from "@jxsuite/ai/harness";
|
|
24
|
+
import { DEFAULT_TURN_POLICY, assistantActorId, runTurn } from "@jxsuite/ai/harness";
|
|
23
25
|
import type { TurnInput, TurnRun } from "@jxsuite/ai/harness";
|
|
24
26
|
import { toChatMessages, toOpenAITools } from "@jxsuite/ai/messages";
|
|
25
27
|
import type { StreamingClient } from "@jxsuite/ai/streaming-client";
|
|
@@ -27,7 +29,7 @@ import { createSessionFacts } from "@jxsuite/ai/tools";
|
|
|
27
29
|
import type { SessionFacts, ToolRegistry } from "@jxsuite/ai/tools";
|
|
28
30
|
|
|
29
31
|
import type { Tab } from "../tabs/tab";
|
|
30
|
-
import {
|
|
32
|
+
import { openBatchLane } from "../tabs/transact";
|
|
31
33
|
import { estimatePromptTokens } from "./context-manager";
|
|
32
34
|
import { fileTurn, openTurnLedger, turnAnchor } from "./ai-writes";
|
|
33
35
|
import { applyHarnessEvent } from "./harness/chat-reducer";
|
|
@@ -48,6 +50,8 @@ interface RunAgentLoopOptions {
|
|
|
48
50
|
getTab?: () => Tab | null;
|
|
49
51
|
/** The conversation's facts, which every call's context carries; a fresh set when omitted. */
|
|
50
52
|
session?: SessionFacts;
|
|
53
|
+
/** Where a call that needs the author waits on them. Without one, such a call is refused. */
|
|
54
|
+
interactions?: InteractionStore;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
/**
|
|
@@ -62,26 +66,23 @@ export async function runAgentLoop({
|
|
|
62
66
|
signal,
|
|
63
67
|
getTab,
|
|
64
68
|
session = createSessionFacts(),
|
|
69
|
+
interactions,
|
|
65
70
|
}: RunAgentLoopOptions): Promise<void> {
|
|
66
71
|
/* The ledger is filed under the turn's last DRAWN assistant message (see services/ai-writes), so
|
|
67
72
|
the scan starts at the message this turn answers. */
|
|
68
73
|
const turnUserId = chatState.messages.findLast((m) => m.role === "user")?.id;
|
|
69
74
|
const ledger = openTurnLedger(`turn:${chatState.messages.length}`);
|
|
70
75
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
endBatch();
|
|
82
|
-
beginBatch(current);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
76
|
+
/* One undo step per document the turn writes. A loop with no `getTab` has no document of its own
|
|
77
|
+
(the evals), so it gets no lane and each tool is its own entry. The owner's id is the one each
|
|
78
|
+
call's context will carry, so a tool's write joins the lane by naming `ctx.actor`. */
|
|
79
|
+
const lane = getTab
|
|
80
|
+
? openBatchLane({
|
|
81
|
+
id: assistantActorId(session.sessionId, ledger.turnId),
|
|
82
|
+
kind: "assistant",
|
|
83
|
+
turnId: ledger.turnId,
|
|
84
|
+
})
|
|
85
|
+
: null;
|
|
85
86
|
|
|
86
87
|
/* The placeholder `sendMessage` pushed is round one's message, so the turn answers into it; the
|
|
87
88
|
conversation the turn continues is everything before it. */
|
|
@@ -124,7 +125,6 @@ export async function runAgentLoop({
|
|
|
124
125
|
superseded = true;
|
|
125
126
|
run?.cancel("superseded");
|
|
126
127
|
},
|
|
127
|
-
reanchor: reanchorBatch,
|
|
128
128
|
}),
|
|
129
129
|
session,
|
|
130
130
|
ledger,
|
|
@@ -137,13 +137,16 @@ export async function runAgentLoop({
|
|
|
137
137
|
if (signal) {
|
|
138
138
|
input.signal = signal;
|
|
139
139
|
}
|
|
140
|
+
if (interactions) {
|
|
141
|
+
input.interactions = interactions.strategy;
|
|
142
|
+
}
|
|
140
143
|
if (placeholder) {
|
|
141
144
|
input.firstMessageId = placeholder.id;
|
|
142
145
|
}
|
|
143
146
|
run = runTurn(input);
|
|
144
147
|
await run.outcome;
|
|
145
148
|
} finally {
|
|
146
|
-
|
|
147
|
-
fileTurn(turnAnchor(chatState.messages, turnUserId) ?? "", ledger.writes);
|
|
149
|
+
const batches = lane?.close() ?? [];
|
|
150
|
+
fileTurn(turnAnchor(chatState.messages, turnUserId) ?? "", ledger.writes, batches);
|
|
148
151
|
}
|
|
149
152
|
}
|
package/src/state.ts
CHANGED
|
@@ -154,19 +154,9 @@ export function pathsEqual(a: JxPath | null, b: JxPath | null) {
|
|
|
154
154
|
return a.every((v, i) => v === b[i]);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
* @param {JxPath} path
|
|
161
|
-
* @param {JxPath} descendant
|
|
162
|
-
* @returns {boolean}
|
|
163
|
-
*/
|
|
164
|
-
export function isAncestor(path: JxPath, descendant: JxPath) {
|
|
165
|
-
if (path.length > descendant.length) {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
return path.every((v, i) => v === descendant[i]);
|
|
169
|
-
}
|
|
157
|
+
/* Lives beside the doc-op vocabulary (`@jxsuite/schema/doc-ops`), with the other structural-path
|
|
158
|
+
helpers; re-exported for the many callers that import it from here. */
|
|
159
|
+
export { isAncestor } from "@jxsuite/schema/doc-ops";
|
|
170
160
|
|
|
171
161
|
// ─── Tree flattening (for layer panel) ────────────────────────────────────────
|
|
172
162
|
|
|
@@ -136,6 +136,18 @@
|
|
|
136
136
|
}
|
|
137
137
|
]
|
|
138
138
|
},
|
|
139
|
+
"address": {
|
|
140
|
+
"tagName": "div",
|
|
141
|
+
"attributes": {
|
|
142
|
+
"part": "empty"
|
|
143
|
+
},
|
|
144
|
+
"children": [
|
|
145
|
+
{
|
|
146
|
+
"tagName": "p",
|
|
147
|
+
"textContent": "Add ?project= and a project folder's absolute path to this page's address to open it."
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
},
|
|
139
151
|
"welcome": {
|
|
140
152
|
"tagName": "div",
|
|
141
153
|
"attributes": {
|
|
@@ -81,8 +81,11 @@ export interface FileRowView {
|
|
|
81
81
|
|
|
82
82
|
/** Everything the document draws, as one value. */
|
|
83
83
|
export interface FilesPanelValues {
|
|
84
|
-
/**
|
|
85
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Which of four things the panel is: no project, a monorepo root on a host that can open a
|
|
86
|
+
* project, the same root on one that opens from the address (`?project=`), or a tree.
|
|
87
|
+
*/
|
|
88
|
+
view: "none" | "welcome" | "address" | "tree";
|
|
86
89
|
headerState: "hidden" | "shown";
|
|
87
90
|
projectName: string;
|
|
88
91
|
query: string;
|
package/src/surfaces/welcome.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { getAccountStatus, getBannerNotices, needsAppInstall } from "../account-status";
|
|
23
23
|
import { runOfferedAction } from "../account/action-flow";
|
|
24
24
|
import { platformSupportsAddRepo } from "../new-project/add-repo-modal";
|
|
25
|
+
import { canOpenProject, getPlatform } from "../platform";
|
|
25
26
|
import { getProjectList } from "../project-list";
|
|
26
27
|
import { reactive } from "../reactivity";
|
|
27
28
|
import { MINUTE, now } from "../services/clock";
|
|
@@ -226,7 +227,9 @@ function project(): Omit<WelcomeScope, "run" | "open" | "remove" | "clear" | "no
|
|
|
226
227
|
// One entry, not two: the starter gallery IS the first step of New Project now, so the
|
|
227
228
|
// Separate "Start from an Example…" button it used to hide behind is gone.
|
|
228
229
|
{ icon: "plus", id: "new", title: "New Project…" },
|
|
229
|
-
|
|
230
|
+
...(canOpenProject(getPlatform())
|
|
231
|
+
? [{ icon: "folder-open", id: "open", title: "Open Project…" } as const]
|
|
232
|
+
: []),
|
|
230
233
|
...(platformSupportsClone()
|
|
231
234
|
? [{ icon: "download-simple", id: "clone", title: "Clone Git Repository…" } as const]
|
|
232
235
|
: []),
|
package/src/tabs/selection.ts
CHANGED
|
@@ -42,10 +42,22 @@
|
|
|
42
42
|
* standing between a mixed selection and a half-applied, unrecorded delete.
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
import {
|
|
45
|
+
import { isAncestor } from "@jxsuite/schema/doc-ops";
|
|
46
46
|
|
|
47
47
|
import type { JxPath } from "../state";
|
|
48
|
-
import {
|
|
48
|
+
import { pathsEqual } from "../state";
|
|
49
|
+
|
|
50
|
+
/* The structural-path helpers live beside the doc-op vocabulary, so any host that edits a document
|
|
51
|
+
the way Studio does orders and filters a batch the same way. Re-exported here, where every
|
|
52
|
+
Studio caller already imports them. */
|
|
53
|
+
export {
|
|
54
|
+
comparePaths,
|
|
55
|
+
isSpliceablePath,
|
|
56
|
+
spliceCoordinate,
|
|
57
|
+
structuralBatch,
|
|
58
|
+
topLevelSelection,
|
|
59
|
+
} from "@jxsuite/schema/doc-ops";
|
|
60
|
+
export type { SpliceCoordinate } from "@jxsuite/schema/doc-ops";
|
|
49
61
|
|
|
50
62
|
/**
|
|
51
63
|
* The path single-target surfaces address — the most recently added, or `null` when empty.
|
|
@@ -171,113 +183,6 @@ export function pruneSelection(selection: readonly JxPath[], removed: JxPath): J
|
|
|
171
183
|
return clonePaths(selection.filter((p) => !isAncestor(removed, p)));
|
|
172
184
|
}
|
|
173
185
|
|
|
174
|
-
/**
|
|
175
|
-
* Order two paths the way the document reads: parents before children, siblings by index.
|
|
176
|
-
*
|
|
177
|
-
* Segments are compared pairwise; numbers numerically (so `children/10` follows `children/9`), and
|
|
178
|
-
* anything else by string order. A prefix sorts before what extends it.
|
|
179
|
-
*
|
|
180
|
-
* @param {JxPath} a
|
|
181
|
-
* @param {JxPath} b
|
|
182
|
-
* @returns {number}
|
|
183
|
-
*/
|
|
184
|
-
export function comparePaths(a: JxPath, b: JxPath): number {
|
|
185
|
-
const len = Math.min(a.length, b.length);
|
|
186
|
-
for (let i = 0; i < len; i++) {
|
|
187
|
-
const x = a[i]!;
|
|
188
|
-
const y = b[i]!;
|
|
189
|
-
if (x === y) {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (isNumber(x) && isNumber(y)) {
|
|
193
|
-
return x - y;
|
|
194
|
-
}
|
|
195
|
-
return String(x) < String(y) ? -1 : 1;
|
|
196
|
-
}
|
|
197
|
-
return a.length - b.length;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Drop any path whose ancestor is also selected.
|
|
202
|
-
*
|
|
203
|
-
* Deleting a `<section>` and one of its own paragraphs is one deletion, not two: the paragraph goes
|
|
204
|
-
* with its parent, and a second splice into coordinates that no longer exist is how a batch
|
|
205
|
-
* corrupts a document.
|
|
206
|
-
*
|
|
207
|
-
* @param {readonly JxPath[]} selection
|
|
208
|
-
* @returns {JxPath[]}
|
|
209
|
-
*/
|
|
210
|
-
export function topLevelSelection(selection: readonly JxPath[]): JxPath[] {
|
|
211
|
-
return clonePaths(
|
|
212
|
-
selection.filter((p) => !selection.some((q) => !pathsEqual(q, p) && isAncestor(q, p))),
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Whether `path` names a position a structural verb can splice — a tail of `"children"` + index.
|
|
218
|
-
*
|
|
219
|
-
* Every structural mutator (`mutateRemoveNode`, `mutateDuplicateNode`, `mutateWrapNode`) reaches
|
|
220
|
-
* its parent with `parentElementPath` — drop the last two segments — and its position with
|
|
221
|
-
* `childIndex` — take the last. That produces a splice coordinate for exactly one path shape, and
|
|
222
|
-
* the Outline emits three others as first-class rows: the document element (`[]`), a repeater's map
|
|
223
|
-
* template (`[…, "children", 1, "map"]`) and a `$switch` case (`[…, "cases", "warn"]`). For the
|
|
224
|
-
* template, `parentElementPath` resolves to the enclosing children ARRAY, whose `.children` is
|
|
225
|
-
* `undefined`, so the splice throws mid-transaction; for a case it splices at `NaN` and takes the
|
|
226
|
-
* wrong child.
|
|
227
|
-
*
|
|
228
|
-
* Selecting one of those beside an ordinary element is a single ctrl-click, so this is the
|
|
229
|
-
* invariant that keeps a batch from mutating the document and then throwing before anything is
|
|
230
|
-
* recorded.
|
|
231
|
-
*
|
|
232
|
-
* @param {JxPath} path
|
|
233
|
-
* @returns {boolean}
|
|
234
|
-
*/
|
|
235
|
-
export function isSpliceablePath(path: JxPath): boolean {
|
|
236
|
-
return spliceCoordinate(path) !== null;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/** Where a structural verb splices: the parent element's path and the index in its children. */
|
|
240
|
-
export interface SpliceCoordinate {
|
|
241
|
-
parentPath: JxPath;
|
|
242
|
-
index: number;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* The splice coordinate `path` names, or `null` for no path and for every path
|
|
247
|
-
* {@link isSpliceablePath} refuses.
|
|
248
|
-
*
|
|
249
|
-
* @param {JxPath | null | undefined} path
|
|
250
|
-
* @returns {SpliceCoordinate | null}
|
|
251
|
-
*/
|
|
252
|
-
export function spliceCoordinate(path: JxPath | null | undefined): SpliceCoordinate | null {
|
|
253
|
-
const index = path?.at(-1);
|
|
254
|
-
return path && path.at(-2) === "children" && isNumber(index)
|
|
255
|
-
? { index, parentPath: path.slice(0, -2) }
|
|
256
|
-
: null;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* The order a structural batch must run in: contained paths dropped, unspliceable paths dropped,
|
|
261
|
-
* then LAST node first.
|
|
262
|
-
*
|
|
263
|
-
* Descending document order is what makes a loop of splices safe. Removing or duplicating at index
|
|
264
|
-
* `n` renumbers every sibling after `n`, so a batch that starts at the end never touches a
|
|
265
|
-
* coordinate a later step still needs. It is also why duplicating a multi-selection inserts each
|
|
266
|
-
* clone directly after its own original instead of walking the whole run forward.
|
|
267
|
-
*
|
|
268
|
-
* Containment is decided BEFORE spliceability, on the selection the user actually made: a selected
|
|
269
|
-
* repeater template still shadows its own descendants, so selecting the template and something
|
|
270
|
-
* inside it yields nothing rather than quietly deleting the child the user never pointed at.
|
|
271
|
-
*
|
|
272
|
-
* @param {readonly JxPath[]} selection
|
|
273
|
-
* @returns {JxPath[]} Deepest/last first, every entry a splice coordinate.
|
|
274
|
-
*/
|
|
275
|
-
export function structuralBatch(selection: readonly JxPath[]): JxPath[] {
|
|
276
|
-
return topLevelSelection(selection)
|
|
277
|
-
.filter((path) => isSpliceablePath(path))
|
|
278
|
-
.toSorted((a, b) => comparePaths(b, a));
|
|
279
|
-
}
|
|
280
|
-
|
|
281
186
|
/**
|
|
282
187
|
* Deduplicate a list of paths, keeping the FIRST occurrence of each.
|
|
283
188
|
*
|
package/src/tabs/tab.ts
CHANGED
|
@@ -132,7 +132,7 @@ export interface StoredLivePreview {
|
|
|
132
132
|
error: string | null;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
interface HistorySnapshot {
|
|
135
|
+
export interface HistorySnapshot {
|
|
136
136
|
/** Full document at this state (checkpoint), or null when the ops describe the transition. */
|
|
137
137
|
document: JxMutableNode | null;
|
|
138
138
|
/** Selection after this state's transaction — the whole set, so a batch undoes back to it. */
|
|
@@ -153,6 +153,13 @@ interface HistorySnapshot {
|
|
|
153
153
|
* back through the prose one pause at a time instead of undoing the edit.
|
|
154
154
|
*/
|
|
155
155
|
coalesceKey?: string | null;
|
|
156
|
+
/**
|
|
157
|
+
* Who made this step, when it is not the person at the keyboard: the id of the actor whose batch
|
|
158
|
+
* lane closed it (specs/ai.md §3.3). Absent on a person's own edits.
|
|
159
|
+
*/
|
|
160
|
+
actor?: string;
|
|
161
|
+
/** The turn whose batch this step is, beside {@link HistorySnapshot.actor}. */
|
|
162
|
+
turnId?: string;
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
/**
|
|
@@ -186,6 +193,8 @@ export interface Tab {
|
|
|
186
193
|
scope: {
|
|
187
194
|
stop: () => void;
|
|
188
195
|
run: <T>(fn: () => T) => T | undefined;
|
|
196
|
+
/** False once {@link disposeTab} stopped it: how a record that outlives the tab can tell. */
|
|
197
|
+
readonly active?: boolean;
|
|
189
198
|
};
|
|
190
199
|
doc: {
|
|
191
200
|
document: JxMutableNode;
|