@yugoo-design/dsh-design-studio-dsh 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +427 -3
- package/lib/authoring/panel-components.json +10790 -0
- package/lib/authoring/studio-creators/material.json +1 -0
- package/lib/authoring-agent-Ci08ifdJ.js +634 -0
- package/lib/authoring-agent.d.ts +24 -0
- package/lib/authoring-agent.js +2 -0
- package/lib/authoring-agent.types-QLa61eE6.d.ts +88 -0
- package/lib/authoring-entry.d.ts +20 -0
- package/lib/authoring-entry.js +77 -0
- package/lib/authoring-host-BlsA6wXz.js +347 -0
- package/lib/authoring-host.d.ts +5 -0
- package/lib/authoring-host.js +2 -0
- package/lib/authoring-host.types-d5UT0WEk.d.ts +77 -0
- package/lib/authoring-reference-store.d.ts +39 -0
- package/lib/authoring-reference-store.js +385 -0
- package/lib/authoring-service-qGEk-IE_.js +342 -0
- package/lib/authoring-service.d.ts +112 -0
- package/lib/authoring-service.js +2 -0
- package/lib/authoring-session.d.ts +5 -0
- package/lib/authoring-session.js +203 -0
- package/lib/authoring-session.types-MHEaEFkO.d.ts +58 -0
- package/lib/authoring-sources-BzRuEuub.d.ts +17 -0
- package/lib/authoring-sources.d.ts +2 -0
- package/lib/authoring-sources.js +85 -0
- package/lib/authoring-workbench.d.ts +15 -0
- package/lib/authoring-workbench.js +782 -0
- package/lib/authoring.page-rpc-Bg6VAT0Z.d.ts +30 -0
- package/lib/authoring.page-rpc.d.ts +2 -0
- package/lib/authoring.page-rpc.js +55 -0
- package/lib/authoring.remote-client.d.ts +2557 -0
- package/lib/authoring.remote-client.js +435 -0
- package/lib/client-adapter-BgD1mpCC.d.ts +65 -0
- package/lib/client-adapter.d.ts +2 -64
- package/lib/client.js +1 -2
- package/lib/css-assets/dsh-design-studio-page/9178281a0695f79ff8bff8eca529231b67f896dc1c12c8dbaaad24b32dd3fc07.eot +0 -0
- package/lib/css-assets/dsh-design-studio-page/b366faf39f6bcc48f10b10f2860e5ebe57091cb2a15beeed8a0977e24560bbba.svg +224 -0
- package/lib/css-assets/dsh-design-studio-page/manifest.json +20 -0
- package/lib/index.js +4 -105
- package/lib/kit-files-DKBlBOG8.js +159 -0
- package/lib/page-client.js +4 -23849
- package/lib/page-path-Bj5jO20L.d.ts +25 -0
- package/lib/page-path.d.ts +2 -2
- package/lib/page-path.js +70 -2
- package/lib/page-rpc-DiazVY00.d.ts +24 -0
- package/lib/page-rpc.d.ts +2 -15
- package/lib/page-rpc.js +9 -4
- package/lib/page.d.ts +5 -3
- package/lib/page.js +219 -2
- package/lib/remote-method-ab_lHwWN.js +16 -0
- package/lib/typert.host.d.ts +827 -2
- package/lib/typert.host.js +2 -1
- package/lib/typert.remote-client-CWz-anTr.d.ts +37 -0
- package/lib/typert.remote-client.d.ts +1 -36
- package/licenses/dsh-distribution.json +8644 -149
- package/package.json +73 -10
- package/lib/page-path-wn8e_fid.d.ts +0 -14
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
import { isAbsolute } from "node:path";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { foldConsumedWork } from "@deepseek-ai/dsh-agent";
|
|
5
|
+
import { SessionId } from "@deepseek-ai/dsh-session";
|
|
6
|
+
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
7
|
+
import { appendDelegatedPolicyOverrides, applyChildComposition, captureDelegatedPolicyOverrides, resolveChildAgentOptions, resolveChildDepth } from "@deepseek-ai/dsh-subagent";
|
|
8
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
9
|
+
import { open } from "node:fs/promises";
|
|
10
|
+
import { PANEL_COMPONENT_CATALOG, queryPanelAuthoringReference } from "@yugoo-design/dsh-design-core/dsl";
|
|
11
|
+
//#region src/authoring-material.ts
|
|
12
|
+
/** Package-owned creator methods; model arguments select map keys, never files. */
|
|
13
|
+
const MAX_MATERIAL_BYTES = 16777216;
|
|
14
|
+
const MAX_RESOURCE_BYTES = 2097152;
|
|
15
|
+
const MAX_TOTAL_BYTES = 4194304;
|
|
16
|
+
const PAGE_BYTES = 16384;
|
|
17
|
+
const record = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
18
|
+
function textPage(bytes, offset = 0) {
|
|
19
|
+
if (!Number.isSafeInteger(offset) || typeof offset !== "number" || offset < 0 || offset > bytes.length || offset < bytes.length && (bytes[offset] & 192) === 128) throw new Error("Invalid authoring resource UTF-8 byte offset");
|
|
20
|
+
let end = Math.min(bytes.length, offset + PAGE_BYTES);
|
|
21
|
+
while (end < bytes.length && (bytes[end] & 192) === 128) end--;
|
|
22
|
+
return {
|
|
23
|
+
offset,
|
|
24
|
+
content: bytes.subarray(offset, end).toString("utf8"),
|
|
25
|
+
bytes: end - offset,
|
|
26
|
+
totalBytes: bytes.length,
|
|
27
|
+
...end < bytes.length ? { nextOffset: end } : {}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function createStudioAuthoringMaterialReader(value) {
|
|
31
|
+
if (!record(value) || value.schemaVersion !== 1 || !Array.isArray(value.skills) || !value.skills.length || !record(value.resources)) throw new Error("Packaged Studio authoring methods are unavailable");
|
|
32
|
+
const entries = Object.entries(value.resources);
|
|
33
|
+
if (!entries.length || entries.length > 128) throw new Error("Studio authoring material exceeds its resource budget");
|
|
34
|
+
const resources = /* @__PURE__ */ new Map();
|
|
35
|
+
let total = 0;
|
|
36
|
+
for (const [path, entry] of entries) {
|
|
37
|
+
if (!path || path.length > 1024 || path.startsWith("/") || path.includes("\\") || path.split("/").some((part) => !part || part === "." || part === "..") || !record(entry) || typeof entry.content !== "string") throw new Error("Invalid packaged Studio authoring resource");
|
|
38
|
+
const bytes = Buffer.from(entry.content, "utf8");
|
|
39
|
+
if (bytes.length !== entry.bytes || bytes.length > MAX_RESOURCE_BYTES || (total += bytes.length) > MAX_TOTAL_BYTES) throw new Error("Studio authoring material exceeds its text budget");
|
|
40
|
+
resources.set(path, bytes);
|
|
41
|
+
}
|
|
42
|
+
if (!resources.has("index.md") || value.skills.some((skill) => !record(skill) || typeof skill.name !== "string" || typeof skill.description !== "string" || typeof skill.method !== "string" || !resources.has(skill.method))) throw new Error("Packaged Studio authoring method index is incomplete");
|
|
43
|
+
const skills = value.skills.map((skill) => ({
|
|
44
|
+
name: skill.name,
|
|
45
|
+
description: skill.description,
|
|
46
|
+
method: skill.method
|
|
47
|
+
}));
|
|
48
|
+
return (input = {}) => {
|
|
49
|
+
if (!record(input) || Reflect.ownKeys(input).some((key) => key !== "resource" && key !== "offset")) throw new Error("Authoring methods accept only an indexed resource and byte offset");
|
|
50
|
+
const resource = input.resource === void 0 ? "index.md" : input.resource, offset = input.offset === void 0 ? 0 : input.offset;
|
|
51
|
+
if (typeof resource !== "string" || !resources.has(resource)) throw new Error("Unknown indexed Studio authoring resource");
|
|
52
|
+
return {
|
|
53
|
+
resource,
|
|
54
|
+
...textPage(resources.get(resource), offset),
|
|
55
|
+
...resource === "index.md" ? { skills: structuredClone(skills) } : {}
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
let packaged;
|
|
60
|
+
async function readPackageJson(path, maxBytes) {
|
|
61
|
+
const handle = await open(new URL("../lib/authoring/" + path, import.meta.url), "r");
|
|
62
|
+
try {
|
|
63
|
+
const bytes = Buffer.alloc(maxBytes + 1);
|
|
64
|
+
let size = 0;
|
|
65
|
+
while (size < bytes.length) {
|
|
66
|
+
const result = await handle.read(bytes, size, bytes.length - size, size);
|
|
67
|
+
if (!result.bytesRead) break;
|
|
68
|
+
size += result.bytesRead;
|
|
69
|
+
}
|
|
70
|
+
if (size > maxBytes) throw new Error("Packaged Studio authoring material exceeds its budget");
|
|
71
|
+
return JSON.parse(bytes.subarray(0, size).toString("utf8"));
|
|
72
|
+
} finally {
|
|
73
|
+
await handle.close();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function readStudioAuthoringMaterial(input = {}) {
|
|
77
|
+
packaged ??= readPackageJson("studio-creators/material.json", MAX_MATERIAL_BYTES).then(createStudioAuthoringMaterialReader);
|
|
78
|
+
return (await packaged)(input);
|
|
79
|
+
}
|
|
80
|
+
let panelMaterial;
|
|
81
|
+
/** Same Core query and build-generated source references as the Companion tool. */
|
|
82
|
+
async function readStudioPanelComponents(input = {}) {
|
|
83
|
+
if (!record(input) || Reflect.ownKeys(input).some((key) => ![
|
|
84
|
+
"name",
|
|
85
|
+
"reference",
|
|
86
|
+
"type",
|
|
87
|
+
"offset"
|
|
88
|
+
].includes(String(key)))) throw new Error("Invalid component reference request");
|
|
89
|
+
const { name, reference, type, offset } = input;
|
|
90
|
+
if (name !== void 0 && (typeof name !== "string" || !name || name.length > 128 || !Object.hasOwn(PANEL_COMPONENT_CATALOG, name))) throw new Error("Unknown panel component; read components:{} for the installed vocabulary");
|
|
91
|
+
if (reference !== void 0 || type !== void 0) {
|
|
92
|
+
if (typeof name !== "string" || ![
|
|
93
|
+
"contract",
|
|
94
|
+
"docs",
|
|
95
|
+
"examples"
|
|
96
|
+
].includes(reference) || type !== void 0 && (reference !== "contract" || typeof type !== "string" || !type || type.length > 256)) throw new Error("Component references require an exact name and contract/docs/examples; type is valid only for contract");
|
|
97
|
+
panelMaterial ??= readPackageJson("panel-components.json", 6e6);
|
|
98
|
+
const result = queryPanelAuthoringReference(await panelMaterial, name, reference, type);
|
|
99
|
+
const bytes = Buffer.from(JSON.stringify(result), "utf8");
|
|
100
|
+
if (bytes.length > 262144) throw new Error("Component authoring response exceeds its 256 KiB budget; follow an exact contract type");
|
|
101
|
+
return bytes.length > PAGE_BYTES || offset !== void 0 ? {
|
|
102
|
+
component: name,
|
|
103
|
+
reference,
|
|
104
|
+
format: "json",
|
|
105
|
+
...textPage(bytes, offset)
|
|
106
|
+
} : result;
|
|
107
|
+
}
|
|
108
|
+
if (offset !== void 0) throw new Error("Component offsets require an exact reference");
|
|
109
|
+
return {
|
|
110
|
+
components: Object.entries(PANEL_COMPONENT_CATALOG).filter(([kind]) => name === void 0 || kind === name).map(([kind, entry]) => ({
|
|
111
|
+
name: kind,
|
|
112
|
+
...entry,
|
|
113
|
+
generated: "autoOrder" in entry && entry.autoOrder !== void 0
|
|
114
|
+
})),
|
|
115
|
+
contract: "Runtime vocabulary and composition guidance only; not a complete parameter schema or standard A2UI specification."
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const STUDIO_AUTHORING_METHOD_GUIDANCE = "Before proposing Kit/Bundle source changes, read the packaged creator method index and the applicable complete methods and linked resources. Follow every nextOffset until each required resource is complete; index summaries do not replace methods. Read the exact working copy and its own method/output contract too. Preserve its existing catalog identity, runtime name and version in their respective source files; display titles do not rename those identities. For Panel changes, read authoring:{resource:\"source/docs/dsl-spec.md\"} for the complete manifest, including required root fields; a component contract describes only part of that manifest. Discover components:{} and read the selected exact component with components:{name,reference:\"contract\"}, then its docs/examples and referenced type declarations before choosing parameters. Large component references return JSON text pages: keep the same selectors and follow nextOffset with components.offset until complete. Follow the returned selectors for allowed values instead of guessing from component names. When repairing diagnostics, retain already-correct changes and replace only the diagnosed assumptions. If a method or component contract is unavailable, report the gap instead of inventing it.";
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/authoring-agent.ts
|
|
121
|
+
/** Restricted native Studio authoring; candidate preparation never saves content. */
|
|
122
|
+
const MAX_EVENTS = 8192;
|
|
123
|
+
const PREPARE_REPLACEMENT_GUIDANCE = "Each prepare starts from the bound saved revision and replaces the entire previous unsaved proposal; calls do not accumulate. When correcting diagnostics, resend the full set of all still-intended unsaved file replacements, including unchanged files from the previous proposal.";
|
|
124
|
+
const clone = (value) => structuredClone(value);
|
|
125
|
+
const encode = (value) => JSON.stringify(value);
|
|
126
|
+
function snapshotScope(value) {
|
|
127
|
+
const scope = clone(value);
|
|
128
|
+
assert.ok(typeof scope.id === "string" && scope.id.length > 0);
|
|
129
|
+
assert.ok(Number.isSafeInteger(scope.expectedRevision) && scope.expectedRevision >= 0);
|
|
130
|
+
assert.ok(Array.isArray(scope.allowedPaths) && scope.allowedPaths.length > 0 && scope.allowedPaths.every((path) => typeof path === "string" && path.length > 0));
|
|
131
|
+
Object.freeze(scope.allowedPaths);
|
|
132
|
+
if (scope.bundle) Object.freeze(scope.bundle);
|
|
133
|
+
return Object.freeze(scope);
|
|
134
|
+
}
|
|
135
|
+
function messageBinding(message) {
|
|
136
|
+
if (message.source.kind !== "yugoo-design") return;
|
|
137
|
+
return message.source.studioAuthoring;
|
|
138
|
+
}
|
|
139
|
+
/** Inbox state is needed to distinguish unrun cancellation from an empty turn. */
|
|
140
|
+
function account(events, reference) {
|
|
141
|
+
const queues = /* @__PURE__ */ new Map(), owned = events.filter((event) => event.seq > reference.afterSeq);
|
|
142
|
+
let turn = null, consumedTurn = null, dropped = false, bound = false;
|
|
143
|
+
for (const event of events) {
|
|
144
|
+
if (event.type === "turn/start" && event.seq > reference.afterSeq) turn = event.data.turn;
|
|
145
|
+
if (event.type === "agent/inbox/spliced") {
|
|
146
|
+
const data = event.data, queue = queues.get(data.target) ?? [];
|
|
147
|
+
const removed = queue.splice(data.start, data.removedCount ?? 0, ...data.inserted);
|
|
148
|
+
queues.set(data.target, queue);
|
|
149
|
+
for (const message of data.inserted) if (message.id === reference.messageId) {
|
|
150
|
+
const { messageId: _id, ...expected } = reference;
|
|
151
|
+
assert.deepEqual(messageBinding(message), expected, "Native Studio binding differs from the retained reference");
|
|
152
|
+
bound = true;
|
|
153
|
+
}
|
|
154
|
+
if (removed.some((message) => message.id === reference.messageId)) {
|
|
155
|
+
if (data.outcome === "canceled") dropped = true;
|
|
156
|
+
else if (turn !== null) consumedTurn = turn;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (event.type === "user/message" && event.data.id === reference.messageId) {
|
|
160
|
+
const { messageId: _id, ...expected } = reference;
|
|
161
|
+
assert.deepEqual(messageBinding(event.data), expected, "Native Studio message binding mismatch");
|
|
162
|
+
bound = true;
|
|
163
|
+
consumedTurn = turn;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const pendingInput = [...queues.values()].some((queue) => queue.length > 0);
|
|
167
|
+
const consumed = consumedTurn !== null;
|
|
168
|
+
const startIndex = owned.findIndex((event) => event.type === "turn/start" && event.data.turn === consumedTurn);
|
|
169
|
+
const endIndex = owned.findIndex((event) => event.type === "turn/end" && event.data.turn === consumedTurn);
|
|
170
|
+
const end = startIndex >= 0 && endIndex >= startIndex ? foldConsumedWork(owned.slice(startIndex, endIndex + 1)).end : void 0;
|
|
171
|
+
const matchingEnd = end && consumed && end.data.turn === consumedTurn ? end : void 0;
|
|
172
|
+
const reason = matchingEnd?.data.reason.kind;
|
|
173
|
+
const interrupted = reason === "interrupted" || reason === "forked";
|
|
174
|
+
return {
|
|
175
|
+
bound,
|
|
176
|
+
status: interrupted ? "unknown" : dropped || reason === "aborted" ? "cancelled" : reason === "completed" ? "completed" : matchingEnd ? "failed" : consumed ? "running" : bound ? "queued" : "unknown",
|
|
177
|
+
consumed,
|
|
178
|
+
turn: consumedTurn ?? turn,
|
|
179
|
+
terminal: !interrupted && (dropped || !!matchingEnd),
|
|
180
|
+
reason,
|
|
181
|
+
pendingInput,
|
|
182
|
+
throughSeq: events.at(-1)?.seq ?? -1
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** Public Host records for this exact request; execution remains native. */
|
|
186
|
+
function conversation(events, ref, state) {
|
|
187
|
+
const rows = [];
|
|
188
|
+
const calls = /* @__PURE__ */ new Map();
|
|
189
|
+
for (const event of events) {
|
|
190
|
+
if (event.seq <= ref.afterSeq) continue;
|
|
191
|
+
if (event.type === "assistant/message" && event.surfaceOp === "append") {
|
|
192
|
+
const text = event.data.message.content.flatMap((block) => block.type === "text" ? [block.text] : []).join("\n").slice(0, 16384);
|
|
193
|
+
if (text) rows.push({
|
|
194
|
+
id: String(event.data.message.id),
|
|
195
|
+
role: "assistant",
|
|
196
|
+
label: "Agent",
|
|
197
|
+
text,
|
|
198
|
+
status: "completed"
|
|
199
|
+
});
|
|
200
|
+
} else if (event.type === "tool/call") {
|
|
201
|
+
const row = {
|
|
202
|
+
id: String(event.data.callId),
|
|
203
|
+
role: "tool",
|
|
204
|
+
label: event.data.name,
|
|
205
|
+
text: "",
|
|
206
|
+
status: "running"
|
|
207
|
+
};
|
|
208
|
+
rows.push(row);
|
|
209
|
+
calls.set(row.id, row);
|
|
210
|
+
} else if (event.type === "tool/result" && event.surfaceOp === "append") {
|
|
211
|
+
const row = calls.get(String(event.data.message.toolCallId));
|
|
212
|
+
if (row) {
|
|
213
|
+
row.status = event.data.message.isError ? "failed" : "completed";
|
|
214
|
+
row.text = event.data.error?.reason?.slice(0, 16384) ?? "";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (state.terminal) {
|
|
219
|
+
for (const row of calls.values()) if (row.status === "running") row.status = state.status === "cancelled" ? "cancelled" : "unknown";
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
rows: rows.slice(-200),
|
|
223
|
+
omitted: Math.max(0, rows.length - 200)
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/** Host-owned adapter. No filesystem store, provider client, or native profile is created. */
|
|
227
|
+
function createStudioAuthoringAgent(ctx) {
|
|
228
|
+
const live = /* @__PURE__ */ new Map();
|
|
229
|
+
const starting = /* @__PURE__ */ new Set();
|
|
230
|
+
const cleanupFailures = /* @__PURE__ */ new Set();
|
|
231
|
+
const lifetime = new AbortController();
|
|
232
|
+
let disposing;
|
|
233
|
+
const durable = /* @__PURE__ */ new Set();
|
|
234
|
+
const key = (ref) => `${ref.sessionId}:${ref.epoch}`;
|
|
235
|
+
async function observe(reference) {
|
|
236
|
+
const ref = clone(reference), lease = await ctx.sessionQuery.observeSession(SessionId(ref.sessionId), { projectionMode: "none" });
|
|
237
|
+
try {
|
|
238
|
+
assert.equal(String(lease.header.id), ref.sessionId);
|
|
239
|
+
assert.equal(String(lease.header.parentSession), ref.parentSessionId, "Native Studio parent changed");
|
|
240
|
+
assert.ok(lease.events.length <= MAX_EVENTS, "Studio native evidence exceeds the bounded observation limit");
|
|
241
|
+
const next = lease.events.find((event) => event.seq > ref.afterSeq && event.type === "agent/inbox/spliced" && event.data.inserted.some((message) => messageBinding(message)?.epoch !== void 0 && messageBinding(message)?.epoch !== ref.epoch));
|
|
242
|
+
const events = next ? lease.events.filter((event) => event.seq < next.seq) : lease.events;
|
|
243
|
+
const { bound, ...state } = account(events, ref);
|
|
244
|
+
return {
|
|
245
|
+
reference: ref,
|
|
246
|
+
...state,
|
|
247
|
+
source: lease.source,
|
|
248
|
+
durable: durable.has(key(ref)),
|
|
249
|
+
conversation: bound ? conversation(events, ref, state) : {
|
|
250
|
+
rows: [],
|
|
251
|
+
omitted: 0
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
} finally {
|
|
255
|
+
lease[Symbol.dispose]();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async function start(request, previous) {
|
|
259
|
+
const { invocationId, workspaceRoot, prompt, signal: callerSignal, onReference, onPrepared } = request;
|
|
260
|
+
const signal = callerSignal ? AbortSignal.any([callerSignal, lifetime.signal]) : lifetime.signal;
|
|
261
|
+
const waitFor = (operation) => new Promise((resolve, reject) => {
|
|
262
|
+
signal.throwIfAborted();
|
|
263
|
+
const onAbort = () => reject(signal.reason);
|
|
264
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
265
|
+
Promise.resolve().then(() => {
|
|
266
|
+
signal.throwIfAborted();
|
|
267
|
+
return operation();
|
|
268
|
+
}).then((value) => {
|
|
269
|
+
signal.removeEventListener("abort", onAbort);
|
|
270
|
+
resolve(value);
|
|
271
|
+
}, (error) => {
|
|
272
|
+
signal.removeEventListener("abort", onAbort);
|
|
273
|
+
reject(error);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
previous = previous === void 0 ? void 0 : clone(previous);
|
|
277
|
+
signal?.throwIfAborted();
|
|
278
|
+
const scope = snapshotScope(request.scope), parent = request.parent;
|
|
279
|
+
assert.equal(ctx.agents.get(parent.id), parent, "Studio requires the exact live native parent");
|
|
280
|
+
assert.ok(isAbsolute(workspaceRoot), "Studio requires an explicit isolated native workspace");
|
|
281
|
+
assert.ok(typeof invocationId === "string" && invocationId.length > 0);
|
|
282
|
+
assert.ok(typeof prompt === "string" && prompt.trim() && Buffer.byteLength(prompt) <= 131072);
|
|
283
|
+
const read = request.ports.read.bind(request.ports), prepare = request.ports.prepare.bind(request.ports);
|
|
284
|
+
const checked = await waitFor(() => read({ paths: [scope.allowedPaths[0]] }));
|
|
285
|
+
assert.deepEqual(checked.scope, scope, "S3 authoring scope differs from the native binding");
|
|
286
|
+
signal?.throwIfAborted();
|
|
287
|
+
assert.equal(ctx.agents.get(parent.id), parent, "Studio caller closed during source preflight");
|
|
288
|
+
if (previous) {
|
|
289
|
+
assert.equal(previous.parentSessionId, String(parent.id), "Studio resume parent changed");
|
|
290
|
+
assert.equal(previous.scope.id, scope.id, "Studio resume cannot select another instance");
|
|
291
|
+
assert.ok(scope.expectedRevision >= previous.scope.expectedRevision, "Studio resume cannot restore an older source revision");
|
|
292
|
+
const prior = await waitFor(() => observe(previous));
|
|
293
|
+
assert.ok(!prior.pendingInput && prior.consumed, "Native delivery is unconsumed or unknown; do not redispatch it");
|
|
294
|
+
assert.equal(ctx.agents.get(SessionId(previous.sessionId)), void 0, "Native Studio session is already live");
|
|
295
|
+
}
|
|
296
|
+
const sessionId = previous?.sessionId ?? randomUUID(), epoch = randomUUID();
|
|
297
|
+
const events = [], staged = /* @__PURE__ */ new Map();
|
|
298
|
+
let child, handle, ref;
|
|
299
|
+
let currentTurn = null, finished = false, prepared, diagnostic;
|
|
300
|
+
let overflow = false, disposed;
|
|
301
|
+
const policy = captureDelegatedPolicyOverrides(parent), depth = resolveChildDepth(parent, void 0);
|
|
302
|
+
const disposeOwned = () => disposed ??= (async () => {
|
|
303
|
+
try {
|
|
304
|
+
await handle?.dispose();
|
|
305
|
+
} catch (error) {
|
|
306
|
+
cleanupFailures.add(error);
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
})();
|
|
310
|
+
const onAbort = () => queueMicrotask(() => {
|
|
311
|
+
if (!finished && child && ctx.agents.get(child.id) === child) child.cancel({ kind: "parent" });
|
|
312
|
+
});
|
|
313
|
+
const setup = (childCtx, agent) => {
|
|
314
|
+
signal.throwIfAborted();
|
|
315
|
+
assert.equal(agent.session.header.cwd, workspaceRoot, "Native Studio workspace changed; explicit migration is required");
|
|
316
|
+
child = agent;
|
|
317
|
+
appendDelegatedPolicyOverrides(agent.session, policy);
|
|
318
|
+
appendDelegatedPolicyOverrides(agent.session, {
|
|
319
|
+
sandboxMode: "read-only",
|
|
320
|
+
approvalPolicy: "never",
|
|
321
|
+
permissionPreset: void 0
|
|
322
|
+
});
|
|
323
|
+
applyChildComposition(childCtx, parent, { toolFilter: { allow: [] } });
|
|
324
|
+
childCtx.tools.presentAs("native");
|
|
325
|
+
childCtx.on("session/event", (session, event) => {
|
|
326
|
+
if (session !== agent.session) return;
|
|
327
|
+
if (event.type === "turn/start") currentTurn = event.data.turn;
|
|
328
|
+
if (events.length < MAX_EVENTS) events.push(event);
|
|
329
|
+
else overflow = true;
|
|
330
|
+
});
|
|
331
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
332
|
+
const register = (definition) => {
|
|
333
|
+
Object.freeze(definition);
|
|
334
|
+
definitions.set(definition.name, definition);
|
|
335
|
+
childCtx.tools.register(definition);
|
|
336
|
+
};
|
|
337
|
+
const output = {
|
|
338
|
+
schema: {
|
|
339
|
+
type: "object",
|
|
340
|
+
properties: { content: {
|
|
341
|
+
type: "string",
|
|
342
|
+
required: true
|
|
343
|
+
} },
|
|
344
|
+
additionalProperties: false
|
|
345
|
+
},
|
|
346
|
+
render: (_args, value) => [{
|
|
347
|
+
type: "text",
|
|
348
|
+
text: value.content
|
|
349
|
+
}]
|
|
350
|
+
};
|
|
351
|
+
const returned = (value) => {
|
|
352
|
+
const content = encode(value);
|
|
353
|
+
assert.ok(content !== void 0 && Buffer.byteLength(content) <= 5242880, "Studio tool receipt exceeds its bounded budget");
|
|
354
|
+
return { content };
|
|
355
|
+
};
|
|
356
|
+
register(defineTool({
|
|
357
|
+
name: "studio_authoring_read",
|
|
358
|
+
description: "Read the exact bound Studio source, packaged creator methods with authoring:{resource?,offset?}, or actual component references with components:{name?,reference?,type?,offset?}. Read authoring:{} for the index first and follow nextOffset. Discover components:{}, then read an exact name with reference contract/docs/examples; type selects a referenced contract declaration only. Large references are JSON text pages; follow nextOffset with the same selectors and components.offset. These are read-only guidance, not editable source. Scope is fixed by the Host; no project or arbitrary filesystem access.",
|
|
359
|
+
parameters: {
|
|
360
|
+
paths: {
|
|
361
|
+
type: "array",
|
|
362
|
+
items: { type: "string" }
|
|
363
|
+
},
|
|
364
|
+
authoring: {
|
|
365
|
+
type: "object",
|
|
366
|
+
properties: {
|
|
367
|
+
resource: { type: "string" },
|
|
368
|
+
offset: { type: "integer" }
|
|
369
|
+
},
|
|
370
|
+
additionalProperties: false
|
|
371
|
+
},
|
|
372
|
+
components: {
|
|
373
|
+
type: "object",
|
|
374
|
+
properties: {
|
|
375
|
+
name: { type: "string" },
|
|
376
|
+
reference: {
|
|
377
|
+
type: "string",
|
|
378
|
+
enum: [
|
|
379
|
+
"contract",
|
|
380
|
+
"docs",
|
|
381
|
+
"examples"
|
|
382
|
+
]
|
|
383
|
+
},
|
|
384
|
+
type: { type: "string" },
|
|
385
|
+
offset: { type: "integer" }
|
|
386
|
+
},
|
|
387
|
+
additionalProperties: false
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
output,
|
|
391
|
+
async execute(args, exec) {
|
|
392
|
+
exec.signal.throwIfAborted();
|
|
393
|
+
if (args.authoring !== void 0 && Object.keys(args).some((key) => key !== "authoring")) throw new Error("Read authoring methods separately from bound source paths");
|
|
394
|
+
if (args.components !== void 0 && Object.keys(args).some((key) => key !== "components")) throw new Error("Read component references separately from bound source paths");
|
|
395
|
+
const value = args.authoring !== void 0 ? await readStudioAuthoringMaterial(args.authoring) : args.components !== void 0 ? await readStudioPanelComponents(args.components) : await read(args);
|
|
396
|
+
exec.signal.throwIfAborted();
|
|
397
|
+
return returned(value);
|
|
398
|
+
}
|
|
399
|
+
}));
|
|
400
|
+
register(defineTool({
|
|
401
|
+
name: "studio_authoring_prepare",
|
|
402
|
+
description: `Prepare full-file additions and replacements through S1 for human diff/real-preview review. Read, replace or add source files within this working copy. The initial allowedPaths list is a reading focus, not a file-edit restriction. ${PREPARE_REPLACEMENT_GUIDANCE} Never saves or publishes. Finish after a valid candidate.`,
|
|
403
|
+
parameters: { changes: {
|
|
404
|
+
type: "array",
|
|
405
|
+
required: true,
|
|
406
|
+
items: {
|
|
407
|
+
type: "object",
|
|
408
|
+
properties: {
|
|
409
|
+
path: {
|
|
410
|
+
type: "string",
|
|
411
|
+
required: true
|
|
412
|
+
},
|
|
413
|
+
content: {
|
|
414
|
+
type: "string",
|
|
415
|
+
required: true
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
additionalProperties: false
|
|
419
|
+
}
|
|
420
|
+
} },
|
|
421
|
+
output,
|
|
422
|
+
async execute(args, exec) {
|
|
423
|
+
exec.signal.throwIfAborted();
|
|
424
|
+
const changes = args.changes.map((change) => {
|
|
425
|
+
assert.ok(typeof change.path === "string" && typeof change.content === "string", "Complete Studio replacement required");
|
|
426
|
+
return {
|
|
427
|
+
path: change.path,
|
|
428
|
+
content: change.content
|
|
429
|
+
};
|
|
430
|
+
});
|
|
431
|
+
const value = await prepare({ changes });
|
|
432
|
+
exec.signal.throwIfAborted();
|
|
433
|
+
const response = returned(value);
|
|
434
|
+
staged.set(exec.token, clone(value));
|
|
435
|
+
return response;
|
|
436
|
+
}
|
|
437
|
+
}));
|
|
438
|
+
childCtx.tools.guard((exec) => signal.aborted || exec.agent !== agent || exec.parent !== void 0 || finished || ctx.agents.get(parent.id) !== parent || !definitions.has(exec.name) || childCtx.tools.get(exec.name, agent) !== definitions.get(exec.name) ? "Operation is outside the exact Studio read/prepare capability" : void 0);
|
|
439
|
+
childCtx.on("tools/result", (exec, result) => {
|
|
440
|
+
if (exec.agent !== agent || !staged.has(exec.token)) return;
|
|
441
|
+
const value = staged.get(exec.token);
|
|
442
|
+
staged.delete(exec.token);
|
|
443
|
+
if (result.isError || exec.signal.aborted || signal?.aborted || ctx.agents.get(parent.id) !== parent) return;
|
|
444
|
+
prepared = value;
|
|
445
|
+
const projectionFailed = () => {
|
|
446
|
+
diagnostic = "Candidate projection failed; the same S3 receipt remains available through readPrepared.";
|
|
447
|
+
};
|
|
448
|
+
try {
|
|
449
|
+
Promise.resolve(onPrepared?.(clone(value))).catch(projectionFailed);
|
|
450
|
+
} catch {
|
|
451
|
+
projectionFailed();
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
const manifestIdentity = scope.bundle ? `For the selected Bundle manifest: Set skill.yaml identity to ${encode(scope.bundle.name)} and version to ${encode(scope.bundle.version)}. Keep skill.yaml name and SKILL.md frontmatter name as the runtime name read from SKILL.md; the catalog identity is separate and must not replace that runtime name.` : "";
|
|
455
|
+
childCtx.systemPrompt.section({
|
|
456
|
+
name: "studio:customization-methods",
|
|
457
|
+
order: 550,
|
|
458
|
+
text: `Work only on this Host-bound Studio source scope: ${encode(scope)}. ${manifestIdentity} ${STUDIO_AUTHORING_METHOD_GUIDANCE} Use studio_authoring_read with {authoring:{}} for the creator index, then {authoring:{resource,offset}} for its exact resources. Read bound source and the actual component catalog separately with {paths:[...]}. Use studio_authoring_prepare for full-file candidates, including new source files in this working copy; allowedPaths names the initial read only, and validation/preview diagnostics come from S1. ${PREPARE_REPLACEMENT_GUIDANCE} No filesystem, shell, project/GRA, save, publication, or delegation is granted. A prepared candidate is for human review, not saved content. Finish your native turn after preparing the requested candidate.`
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
try {
|
|
462
|
+
const common = {
|
|
463
|
+
parentAgent: parent,
|
|
464
|
+
agentOptions: resolveChildAgentOptions(parent, void 0, depth),
|
|
465
|
+
signal,
|
|
466
|
+
setup
|
|
467
|
+
};
|
|
468
|
+
handle = previous ? await parent.ctx.agents.resume({
|
|
469
|
+
...common,
|
|
470
|
+
resumeSessionId: SessionId(sessionId)
|
|
471
|
+
}) : await parent.ctx.agents.create({
|
|
472
|
+
...common,
|
|
473
|
+
sessionId: SessionId(sessionId),
|
|
474
|
+
meta: {
|
|
475
|
+
cwd: workspaceRoot,
|
|
476
|
+
parentSession: parent.session.id,
|
|
477
|
+
delegationDepth: depth,
|
|
478
|
+
isSeeded: false
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
signal.throwIfAborted();
|
|
482
|
+
assert.equal(String(handle.agent.id), sessionId);
|
|
483
|
+
const lease = await ctx.sessionQuery.observeSession(SessionId(sessionId), { projectionMode: "none" });
|
|
484
|
+
let afterSeq;
|
|
485
|
+
try {
|
|
486
|
+
signal.throwIfAborted();
|
|
487
|
+
afterSeq = lease.cursor;
|
|
488
|
+
} finally {
|
|
489
|
+
lease[Symbol.dispose]();
|
|
490
|
+
}
|
|
491
|
+
const binding = {
|
|
492
|
+
sessionId,
|
|
493
|
+
parentSessionId: String(parent.id),
|
|
494
|
+
invocationId,
|
|
495
|
+
epoch,
|
|
496
|
+
afterSeq,
|
|
497
|
+
scope
|
|
498
|
+
};
|
|
499
|
+
const message = createUserMessage({
|
|
500
|
+
source: {
|
|
501
|
+
kind: "yugoo-design",
|
|
502
|
+
studioAuthoring: binding
|
|
503
|
+
},
|
|
504
|
+
content: [{
|
|
505
|
+
type: "text",
|
|
506
|
+
text: prompt
|
|
507
|
+
}]
|
|
508
|
+
});
|
|
509
|
+
ref = Object.freeze({
|
|
510
|
+
...binding,
|
|
511
|
+
messageId: String(message.id)
|
|
512
|
+
});
|
|
513
|
+
await waitFor(() => onReference?.(clone(ref)));
|
|
514
|
+
signal?.throwIfAborted();
|
|
515
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
516
|
+
signal?.throwIfAborted();
|
|
517
|
+
try {
|
|
518
|
+
handle.agent.followup(message);
|
|
519
|
+
} catch {
|
|
520
|
+
diagnostic = "Native dispatch acknowledgement is unknown; do not redispatch this input.";
|
|
521
|
+
}
|
|
522
|
+
} catch (error) {
|
|
523
|
+
await disposeOwned();
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
const reference = ref, owned = handle;
|
|
527
|
+
const result = (async () => {
|
|
528
|
+
try {
|
|
529
|
+
await owned.agent.whenIdle();
|
|
530
|
+
if (await ctx.sessions.flush(owned.agent.session) && ctx.get("sessionPersistence")) {
|
|
531
|
+
durable.add(key(reference));
|
|
532
|
+
if (durable.size > 256) durable.delete(durable.values().next().value);
|
|
533
|
+
}
|
|
534
|
+
const observation = await observe(reference);
|
|
535
|
+
if (overflow) return {
|
|
536
|
+
...observation,
|
|
537
|
+
status: "unknown",
|
|
538
|
+
terminal: false,
|
|
539
|
+
diagnostic: "Native evidence limit exceeded; retain and inspect the original session."
|
|
540
|
+
};
|
|
541
|
+
return diagnostic ? {
|
|
542
|
+
...observation,
|
|
543
|
+
diagnostic
|
|
544
|
+
} : observation;
|
|
545
|
+
} catch {
|
|
546
|
+
return {
|
|
547
|
+
reference,
|
|
548
|
+
status: "unknown",
|
|
549
|
+
consumed: false,
|
|
550
|
+
turn: currentTurn,
|
|
551
|
+
terminal: false,
|
|
552
|
+
throughSeq: events.at(-1)?.seq ?? reference.afterSeq,
|
|
553
|
+
source: "live",
|
|
554
|
+
durable: durable.has(key(reference)),
|
|
555
|
+
pendingInput: true,
|
|
556
|
+
diagnostic: "Native settlement or persistence could not be verified; retain this address and inspect without redispatch."
|
|
557
|
+
};
|
|
558
|
+
} finally {
|
|
559
|
+
finished = true;
|
|
560
|
+
signal?.removeEventListener("abort", onAbort);
|
|
561
|
+
await disposeOwned();
|
|
562
|
+
live.delete(key(reference));
|
|
563
|
+
}
|
|
564
|
+
})();
|
|
565
|
+
result.catch(() => {});
|
|
566
|
+
const run = {
|
|
567
|
+
reference,
|
|
568
|
+
result,
|
|
569
|
+
observe: () => observe(reference),
|
|
570
|
+
readPrepared: () => prepared === void 0 ? void 0 : clone(prepared),
|
|
571
|
+
cancel({ expectedTurn }) {
|
|
572
|
+
if (finished) return result;
|
|
573
|
+
assert.equal(ctx.agents.get(owned.agent.id), owned.agent, "Native Studio handle is no longer owned");
|
|
574
|
+
assert.equal(currentTurn, expectedTurn, "Native Studio turn changed; refresh before stopping");
|
|
575
|
+
owned.agent.cancel({ kind: "user" });
|
|
576
|
+
return result;
|
|
577
|
+
},
|
|
578
|
+
async dispose() {
|
|
579
|
+
if (!finished) owned.agent.cancel({ kind: "disposed" });
|
|
580
|
+
await result;
|
|
581
|
+
await disposeOwned();
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
live.set(key(reference), run);
|
|
585
|
+
return Object.freeze(run);
|
|
586
|
+
}
|
|
587
|
+
async function admit(request, previous) {
|
|
588
|
+
lifetime.signal.throwIfAborted();
|
|
589
|
+
let settled;
|
|
590
|
+
const pending = new Promise((resolve) => {
|
|
591
|
+
settled = resolve;
|
|
592
|
+
});
|
|
593
|
+
starting.add(pending);
|
|
594
|
+
try {
|
|
595
|
+
const run = await start(request, previous);
|
|
596
|
+
if (lifetime.signal.aborted) {
|
|
597
|
+
await run.dispose();
|
|
598
|
+
lifetime.signal.throwIfAborted();
|
|
599
|
+
}
|
|
600
|
+
return run;
|
|
601
|
+
} finally {
|
|
602
|
+
starting.delete(pending);
|
|
603
|
+
settled();
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
/** Permanently close admission, then drain both startup and owned native runs. */
|
|
607
|
+
const dispose = () => {
|
|
608
|
+
if (disposing) return disposing;
|
|
609
|
+
let resolve, reject;
|
|
610
|
+
disposing = new Promise((done, failed) => {
|
|
611
|
+
resolve = done;
|
|
612
|
+
reject = failed;
|
|
613
|
+
});
|
|
614
|
+
lifetime.abort(/* @__PURE__ */ new Error("Studio authoring adapter is disposed"));
|
|
615
|
+
Promise.allSettled([...starting, ...[...live.values()].map((run) => Promise.resolve().then(() => run.dispose()))]).then((settled) => {
|
|
616
|
+
const failures = /* @__PURE__ */ new Set([...cleanupFailures, ...settled.filter((value) => value.status === "rejected").map((value) => value.reason)]);
|
|
617
|
+
if (failures.size) reject(new AggregateError([...failures], "Owned Studio native cleanup failed"));
|
|
618
|
+
else resolve();
|
|
619
|
+
});
|
|
620
|
+
return disposing;
|
|
621
|
+
};
|
|
622
|
+
ctx.effect(() => dispose);
|
|
623
|
+
return Object.freeze({
|
|
624
|
+
start: (request) => admit(request),
|
|
625
|
+
observe,
|
|
626
|
+
dispose,
|
|
627
|
+
resume(request) {
|
|
628
|
+
assert.equal(request.explicit, true, "Native Studio continuation requires explicit authorization");
|
|
629
|
+
return admit(request, request.previous);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
//#endregion
|
|
634
|
+
export { readStudioPanelComponents as i, STUDIO_AUTHORING_METHOD_GUIDANCE as n, readStudioAuthoringMaterial as r, createStudioAuthoringAgent as t };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { a as StudioAuthoringResumeRequest, i as StudioAuthoringRequest, n as StudioAuthoringPorts, o as StudioAuthoringRun, r as StudioAuthoringReference, s as StudioAuthoringScope, t as StudioAuthoringObservation } from "./authoring-agent.types-QLa61eE6.js";
|
|
2
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
//#region src/authoring-agent.d.ts
|
|
4
|
+
declare module '@deepseek-ai/dsh-llm' {
|
|
5
|
+
interface MessageSourceMap {
|
|
6
|
+
'yugoo-design': {
|
|
7
|
+
readonly kind: 'yugoo-design';
|
|
8
|
+
} & ({
|
|
9
|
+
readonly form?: never;
|
|
10
|
+
} | {
|
|
11
|
+
readonly form: 'notice';
|
|
12
|
+
readonly summary: string;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Host-owned adapter. No filesystem store, provider client, or native profile is created. */
|
|
17
|
+
declare function createStudioAuthoringAgent(ctx: Context): Readonly<{
|
|
18
|
+
start: (request: StudioAuthoringRequest) => Promise<StudioAuthoringRun>;
|
|
19
|
+
observe: (reference: StudioAuthoringReference) => Promise<StudioAuthoringObservation>;
|
|
20
|
+
dispose: () => Promise<void>;
|
|
21
|
+
resume(request: StudioAuthoringResumeRequest): Promise<StudioAuthoringRun>;
|
|
22
|
+
}>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { type StudioAuthoringObservation, type StudioAuthoringPorts, type StudioAuthoringReference, type StudioAuthoringRequest, type StudioAuthoringResumeRequest, type StudioAuthoringRun, type StudioAuthoringScope, createStudioAuthoringAgent };
|