@matamt-dbg/dsh-studio 0.4.0 → 0.5.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/client.js +224 -62
- package/index.js +7 -5
- package/package.json +1 -1
- package/skills/studio-pixie-hook-guide/SKILL.md +2 -2
- package/skills/studio-pixie-hook-guide/guide.md +27 -7
package/client.js
CHANGED
|
@@ -97,16 +97,16 @@ function parsePreviewRequest(value) {
|
|
|
97
97
|
const row = value;
|
|
98
98
|
const strings = (input) => Array.isArray(input) && input.every((item) => typeof item === "string");
|
|
99
99
|
if (typeof row.preset !== "string" || !strings(row.providers) || !strings(row.tools) || typeof row.prompt !== "string" || typeof row.model !== "string") throw new Error("Invalid preview request");
|
|
100
|
-
const
|
|
100
|
+
const record3 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
101
101
|
if (row.persona !== void 0 && typeof row.persona !== "string") throw new Error("Invalid persona");
|
|
102
|
-
if (row.sections !== void 0 && (!
|
|
103
|
-
if (row.toolOverrides !== void 0 && (!
|
|
104
|
-
if (row.providerConfigs !== void 0 && (!
|
|
102
|
+
if (row.sections !== void 0 && (!record3(row.sections) || Object.values(row.sections).some((v) => typeof v !== "string"))) throw new Error("Invalid section overrides");
|
|
103
|
+
if (row.toolOverrides !== void 0 && (!record3(row.toolOverrides) || Object.values(row.toolOverrides).some((v) => !record3(v) || Object.keys(v).some((k) => !["description", "parameters"].includes(k)) || v.description !== void 0 && typeof v.description !== "string" || v.parameters !== void 0 && (!record3(v.parameters) || Object.values(v.parameters).some((p) => typeof p !== "string"))))) throw new Error("Invalid tool overrides");
|
|
104
|
+
if (row.providerConfigs !== void 0 && (!record3(row.providerConfigs) || Object.values(row.providerConfigs).some((v) => !record3(v)))) throw new Error("Invalid provider configs");
|
|
105
105
|
if (row.configOverrides !== void 0) validateConfigOverrides(row.configOverrides);
|
|
106
106
|
if (row.execution !== void 0) validateExecutionDefaults(row.execution);
|
|
107
107
|
if (row.configExclusions !== void 0 && (!strings(row.configExclusions) || row.configExclusions.some((id) => !id))) throw new Error("Invalid Config exclusions");
|
|
108
108
|
const filter = row.subagentToolFilter;
|
|
109
|
-
if (filter !== void 0 && (!
|
|
109
|
+
if (filter !== void 0 && (!record3(filter) || !strings(filter.deny) || filter.deny.some((name) => !name))) throw new Error("Invalid subagent tool filter");
|
|
110
110
|
return {
|
|
111
111
|
...row.configOverrides !== void 0 ? { configOverrides: row.configOverrides } : {},
|
|
112
112
|
...row.execution !== void 0 ? { execution: row.execution } : {},
|
|
@@ -1264,7 +1264,7 @@ var POLICY_PLUGIN = `${PACKAGE_NAME}/policy`;
|
|
|
1264
1264
|
var ROLE_PLUGIN = `${PACKAGE_NAME}/roles`;
|
|
1265
1265
|
var BATCH_PLUGIN = `${PACKAGE_NAME}/batch`;
|
|
1266
1266
|
var JUDGE_PLUGIN = `${PACKAGE_NAME}/judge`;
|
|
1267
|
-
var STUDIO_VERSION = "0.
|
|
1267
|
+
var STUDIO_VERSION = "0.5.0";
|
|
1268
1268
|
|
|
1269
1269
|
// src/delegation.ts
|
|
1270
1270
|
var ROLE_TOOL = "subagent_role";
|
|
@@ -3437,7 +3437,7 @@ function GateResultDetails({ result, locale, durations = {}, skipped = [], compa
|
|
|
3437
3437
|
] }),
|
|
3438
3438
|
result.records.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("section", { className: "studio-gate-block", "aria-label": t("helperCalls"), children: [
|
|
3439
3439
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionHead, { title: t("helperCalls") }),
|
|
3440
|
-
result.records.map((
|
|
3440
|
+
result.records.map((record3) => stage(record3.id, record3.result))
|
|
3441
3441
|
] }),
|
|
3442
3442
|
result.forwardError?.verdict === "error" && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Notice, { children: [
|
|
3443
3443
|
t("error"),
|
|
@@ -3469,7 +3469,7 @@ function GateResultDetails({ result, locale, durations = {}, skipped = [], compa
|
|
|
3469
3469
|
] }),
|
|
3470
3470
|
result.records.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("section", { "aria-label": t("helperCalls"), children: [
|
|
3471
3471
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionHead, { title: t("helperCalls") }),
|
|
3472
|
-
result.records.map((
|
|
3472
|
+
result.records.map((record3) => stage(record3.id, record3.result))
|
|
3473
3473
|
] }),
|
|
3474
3474
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionHead, { title: t("forward"), actions: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Badge, { children: t(result.forward.kind) }) }),
|
|
3475
3475
|
result.forward.kind !== "none" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "studio-pixie-answer", children: result.forward.text }),
|
|
@@ -3580,6 +3580,8 @@ var messages2 = {
|
|
|
3580
3580
|
"var-data": ["\uC2A4\uD06C\uB9BD\uD2B8 \uCD9C\uB825 \uB370\uC774\uD130(JSON)", "script output data (JSON)", "\u811A\u672C\u8F93\u51FA\u6570\u636E\uFF08JSON\uFF09"],
|
|
3581
3581
|
confirmReplace: ["\uBC14\uAFB8\uAE30 \uC804\uC5D0 \uD655\uC778", "Confirm before replacing", "\u66FF\u6362\u524D\u786E\u8BA4"],
|
|
3582
3582
|
confirmReplaceHint: ["\uBC14\uAFC0 \uB0B4\uC6A9\uC744 \uBC14\uB85C \uBCF4\uB0B4\uC9C0 \uC54A\uACE0 Pixie \uCE74\uB4DC\uC5D0\uC11C \uD655\uC778 \uD6C4 \uBCF4\uB0C5\uB2C8\uB2E4.", "Holds the replacement on a Pixie card until you confirm.", "\u66FF\u6362\u5185\u5BB9\u5148\u4FDD\u7559\u5728 Pixie \u5361\u7247\uFF0C\u786E\u8BA4\u540E\u518D\u53D1\u9001\u3002"],
|
|
3583
|
+
showInChat: ["\uACB0\uACFC\uB97C \uB300\uD654\uC5D0 \uD45C\uC2DC", "Show the result in the chat", "\u5728\u5BF9\u8BDD\u4E2D\u663E\u793A\u7ED3\u679C"],
|
|
3584
|
+
showInChatHint: ["\uAC8C\uC774\uD2B8\uAC00 \uB05D\uB098\uBA74 \uACB0\uACFC\uB97C \uAC80\uC0AC\uD55C \uB300\uC0C1(\uBA54\uC2DC\uC9C0\xB7\uB3C4\uAD6C \uD638\uCD9C\xB7\uB2F5\uBCC0) \uBC14\uB85C \uC544\uB798\uC5D0 \uBCF4\uC5EC \uC90D\uB2C8\uB2E4. \uBA54\uBAA8\uAC00 \uC788\uC73C\uBA74 \uBA54\uBAA8\uB97C, \uC5C6\uC73C\uBA74 \uD310\uC815\uACFC \uC774\uC720\uB97C \uBCF4\uC5EC \uC90D\uB2C8\uB2E4. \uC138\uC158 \uAE30\uB85D\uC5D0\uB294 \uB0A8\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "Shows the result right under what the gate checked (message, tool call, or answer) when it finishes: the note if there is one, otherwise the verdict and reason. Nothing is added to the session log.", "\u95E8\u63A7\u7ED3\u675F\u540E\u5728\u6240\u68C0\u67E5\u7684\u5BF9\u8C61\uFF08\u6D88\u606F\u3001\u5DE5\u5177\u8C03\u7528\u6216\u56DE\u7B54\uFF09\u4E0B\u65B9\u663E\u793A\u7ED3\u679C\uFF1A\u6709\u5907\u6CE8\u65F6\u663E\u793A\u5907\u6CE8\uFF0C\u5426\u5219\u663E\u793A\u5224\u5B9A\u548C\u539F\u56E0\u3002\u4E0D\u4F1A\u5199\u5165\u4F1A\u8BDD\u8BB0\u5F55\u3002"],
|
|
3583
3585
|
onError: ["Pixie\uAC00 \uC2E4\uD328\uD558\uBA74", "If Pixie fails", "\u82E5 Pixie \u5931\u8D25"],
|
|
3584
3586
|
errorPass: ["\uADF8\uB300\uB85C \uC9C4\uD589", "Continue as usual", "\u7167\u5E38\u7EE7\u7EED"],
|
|
3585
3587
|
errorBlock: ["\uB9C9\uC74C", "Stop it", "\u62E6\u4E0B"],
|
|
@@ -3640,7 +3642,7 @@ var messages2 = {
|
|
|
3640
3642
|
};
|
|
3641
3643
|
function gateTaskText(locale, key, values = {}) {
|
|
3642
3644
|
const text = messages2[key][locale.startsWith("ko") ? 0 : locale.startsWith("zh") ? 2 : 1];
|
|
3643
|
-
return text.replace(/\{(\w+)\}/g, (
|
|
3645
|
+
return text.replace(/\{(\w+)\}/g, (match2, name) => name in values ? String(values[name]) : match2);
|
|
3644
3646
|
}
|
|
3645
3647
|
|
|
3646
3648
|
// src/pixie-gate-runs.tsx
|
|
@@ -3717,9 +3719,9 @@ function GateRunsPanel({ actions, locale }) {
|
|
|
3717
3719
|
const request = ++generation;
|
|
3718
3720
|
updates = /* @__PURE__ */ new Map();
|
|
3719
3721
|
try {
|
|
3720
|
-
const
|
|
3722
|
+
const record3 = await actions.gateRuns();
|
|
3721
3723
|
if (!active || request !== generation) return;
|
|
3722
|
-
const merged2 = new Map(
|
|
3724
|
+
const merged2 = new Map(record3.runs.map((run) => [run.id, run]));
|
|
3723
3725
|
for (const [id, run] of updates) merged2.set(id, run);
|
|
3724
3726
|
setRuns([...merged2.values()]);
|
|
3725
3727
|
setError("");
|
|
@@ -4232,11 +4234,11 @@ function pixieText(locale, key) {
|
|
|
4232
4234
|
var PIXIE_ERROR_CODES = { "pixie/no-model": "errNoModel", "pixie/busy": "errBusy", "pixie/empty": "errEmpty" };
|
|
4233
4235
|
var PIXIE_DETAILED_ERROR_CODES = { "pixie/credentials-refs": "errCredentialsRefs" };
|
|
4234
4236
|
function pixieError(locale, message4) {
|
|
4235
|
-
const
|
|
4236
|
-
const key =
|
|
4237
|
+
const match2 = /^\[(pixie\/[\w-]+)\]\s*/.exec(message4);
|
|
4238
|
+
const key = match2 ? PIXIE_ERROR_CODES[match2[1]] : void 0;
|
|
4237
4239
|
if (key) return pixieText(locale, key);
|
|
4238
|
-
const detailed =
|
|
4239
|
-
return detailed ? `${pixieText(locale, detailed)} (${message4.slice(
|
|
4240
|
+
const detailed = match2 ? PIXIE_DETAILED_ERROR_CODES[match2[1]] : void 0;
|
|
4241
|
+
return detailed ? `${pixieText(locale, detailed)} (${message4.slice(match2[0].length)})` : message4;
|
|
4240
4242
|
}
|
|
4241
4243
|
|
|
4242
4244
|
// src/pixie-tab.tsx
|
|
@@ -4343,9 +4345,9 @@ function PixieTab({ actions, locale }) {
|
|
|
4343
4345
|
void refresh();
|
|
4344
4346
|
}, children: t("retry") }), children: loadError }) });
|
|
4345
4347
|
if (!view) return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StudioSurface, { className: "studio-pixie", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "studio-muted", children: t("loading") }) });
|
|
4346
|
-
const
|
|
4347
|
-
const opened =
|
|
4348
|
-
const shown = opened ??
|
|
4348
|
+
const record3 = view.record;
|
|
4349
|
+
const opened = record3.threads.find((thread) => thread.id === openId);
|
|
4350
|
+
const shown = opened ?? record3.threads.find((thread) => thread.id === record3.activeThreadId) ?? record3.threads.at(-1);
|
|
4349
4351
|
const running = live !== void 0 || view.running;
|
|
4350
4352
|
const configured = Boolean(view.settings.model);
|
|
4351
4353
|
const answering = (thread) => live?.threadId === thread.id || thread.turns.some((turn) => turn.status === "streaming");
|
|
@@ -4377,7 +4379,7 @@ function PixieTab({ actions, locale }) {
|
|
|
4377
4379
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "studio-pixie-list-head", children: [
|
|
4378
4380
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "studio-pixie-list-title", children: [
|
|
4379
4381
|
t("allThreads"),
|
|
4380
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "studio-count", children:
|
|
4382
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "studio-count", children: record3.threads.length })
|
|
4381
4383
|
] }),
|
|
4382
4384
|
configured && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconButton, { label: t("newThread"), onClick: () => setCreating((open) => !open), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PlusIcon, {}) })
|
|
4383
4385
|
] }),
|
|
@@ -4399,14 +4401,14 @@ function PixieTab({ actions, locale }) {
|
|
|
4399
4401
|
void createThread();
|
|
4400
4402
|
}, children: t("create") })
|
|
4401
4403
|
] }),
|
|
4402
|
-
|
|
4403
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "studio-pixie-thread-list", "aria-label": t("threads"), children: [...
|
|
4404
|
+
record3.threads.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { role: "status", className: "studio-muted", children: t("noThreads") }),
|
|
4405
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "studio-pixie-thread-list", "aria-label": t("threads"), children: [...record3.threads].reverse().map((thread) => {
|
|
4404
4406
|
const last = thread.id === live?.threadId && !thread.turns.some((turn) => turn.id === live.turnId) ? live.question : thread.turns.at(-1)?.question;
|
|
4405
4407
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", className: "studio-pixie-thread", "aria-current": thread.id === shown?.id ? "true" : void 0, onClick: () => setOpenId(thread.id), children: [
|
|
4406
4408
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "studio-card-head", children: [
|
|
4407
4409
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "studio-pixie-title", children: [
|
|
4408
4410
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("strong", { children: thread.title }),
|
|
4409
|
-
thread.id ===
|
|
4411
|
+
thread.id === record3.activeThreadId && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "studio-pixie-pin", role: "img", "aria-label": t("activeBadge"), title: t("activeBadge"), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PinIcon, { filled: true }) })
|
|
4410
4412
|
] }),
|
|
4411
4413
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ThreadBadges, { thread, answering: answering(thread), locale })
|
|
4412
4414
|
] }),
|
|
@@ -4421,15 +4423,15 @@ function PixieTab({ actions, locale }) {
|
|
|
4421
4423
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "studio-card-head", children: [
|
|
4422
4424
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "studio-pixie-title", children: [
|
|
4423
4425
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("strong", { children: shown.title }),
|
|
4424
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "studio-pixie-pin-toggle", "data-target": shown.id ===
|
|
4426
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "studio-pixie-pin-toggle", "data-target": shown.id === record3.activeThreadId, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4425
4427
|
IconButton,
|
|
4426
4428
|
{
|
|
4427
|
-
label: shown.id ===
|
|
4428
|
-
pressed: shown.id ===
|
|
4429
|
+
label: shown.id === record3.activeThreadId ? t("activeBadge") : t("makeTarget"),
|
|
4430
|
+
pressed: shown.id === record3.activeThreadId,
|
|
4429
4431
|
onClick: () => {
|
|
4430
|
-
if (shown.id !==
|
|
4432
|
+
if (shown.id !== record3.activeThreadId) void target(shown);
|
|
4431
4433
|
},
|
|
4432
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PinIcon, { filled: shown.id ===
|
|
4434
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PinIcon, { filled: shown.id === record3.activeThreadId })
|
|
4433
4435
|
}
|
|
4434
4436
|
) })
|
|
4435
4437
|
] }),
|
|
@@ -4547,72 +4549,206 @@ function PixieCommandRow({ question, outcome, locale, onOpen, actions }) {
|
|
|
4547
4549
|
|
|
4548
4550
|
// src/pixie-gate-tail.tsx
|
|
4549
4551
|
var import_react15 = __toESM(require("react"), 1);
|
|
4552
|
+
|
|
4553
|
+
// src/pixie-gate-chat-node.ts
|
|
4554
|
+
var GATE_RESULT_NODE = "studio-pixie-gate-result";
|
|
4555
|
+
var ANCHOR_OFFSET = 0.2;
|
|
4556
|
+
var SESSION_LOCATION = Object.freeze({ kind: "session" });
|
|
4557
|
+
var record2 = (value) => !!value && typeof value === "object";
|
|
4558
|
+
var appended = (event) => event.surfaceOp === void 0 || event.surfaceOp === "append";
|
|
4559
|
+
function eventAnchor(event) {
|
|
4560
|
+
if (!appended(event) || !record2(event.data)) return void 0;
|
|
4561
|
+
if (event.type === "tool/result") {
|
|
4562
|
+
const message4 = event.data.message;
|
|
4563
|
+
return record2(message4) && typeof message4.toolCallId === "string" ? { kind: "tool", callId: message4.toolCallId } : void 0;
|
|
4564
|
+
}
|
|
4565
|
+
const source = event.data.source;
|
|
4566
|
+
if (event.type === "user/message" && typeof event.data.id === "string" && record2(source) && (source.kind === "user" || source.kind === "agent-message")) return { kind: "message", messageId: event.data.id };
|
|
4567
|
+
return void 0;
|
|
4568
|
+
}
|
|
4569
|
+
var anchorId = (anchor) => anchor.kind === "tool" ? `tool:${anchor.callId}` : `message:${anchor.messageId}`;
|
|
4570
|
+
var match = (event) => {
|
|
4571
|
+
const anchor = eventAnchor(event);
|
|
4572
|
+
return anchor ? { id: anchorId(anchor), role: "start" } : null;
|
|
4573
|
+
};
|
|
4574
|
+
var gateResultDefinition = {
|
|
4575
|
+
kind: GATE_RESULT_NODE,
|
|
4576
|
+
target: "chat",
|
|
4577
|
+
// Function form: DSH 0.2.0-rc.2 does not accept the event-type table form.
|
|
4578
|
+
match,
|
|
4579
|
+
start: (_context, start) => ({ anchor: eventAnchor(start.event), seq: start.event.seq }),
|
|
4580
|
+
update: (context) => context.state,
|
|
4581
|
+
buildViewNode: (context) => {
|
|
4582
|
+
const state = context.state;
|
|
4583
|
+
if (!state) return null;
|
|
4584
|
+
const location = state.anchor.kind === "message" ? SESSION_LOCATION : context.start?.location ?? context.matches[0]?.location ?? { kind: "unresolved" };
|
|
4585
|
+
const anchorSeq = state.seq + ANCHOR_OFFSET;
|
|
4586
|
+
const current = context.current.get("chat");
|
|
4587
|
+
if (current && current.anchorSeq === anchorSeq && current.location === location) return current;
|
|
4588
|
+
return { key: context.key, kind: GATE_RESULT_NODE, id: context.id, target: "chat", anchorSeq, location, visibility: "visible", data: { anchor: state.anchor } };
|
|
4589
|
+
}
|
|
4590
|
+
};
|
|
4591
|
+
function applyGateFrame(record3, frame) {
|
|
4592
|
+
return frame.type === "session" ? { ...record3, disabledGateIds: frame.disabledGateIds } : { ...record3, runs: [...record3.runs.filter((run) => run.id !== frame.run.id), frame.run] };
|
|
4593
|
+
}
|
|
4594
|
+
function bufferGateFrame(frames, frame) {
|
|
4595
|
+
const same = frames.findIndex((old) => old.type === frame.type && (frame.type === "session" || old.type === "run" && old.run.id === frame.run.id));
|
|
4596
|
+
if (same >= 0) frames.splice(same, 1);
|
|
4597
|
+
frames.push(frame);
|
|
4598
|
+
}
|
|
4599
|
+
function gateRunHub(fetch, connect) {
|
|
4600
|
+
let state;
|
|
4601
|
+
let pending;
|
|
4602
|
+
let generation = 0;
|
|
4603
|
+
let stop;
|
|
4604
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4605
|
+
const load = () => {
|
|
4606
|
+
const id = ++generation;
|
|
4607
|
+
const frames = [];
|
|
4608
|
+
const promise = fetch().then((value) => {
|
|
4609
|
+
const next = frames.reduce(applyGateFrame, value);
|
|
4610
|
+
if (id === generation) {
|
|
4611
|
+
state = next;
|
|
4612
|
+
pending = void 0;
|
|
4613
|
+
}
|
|
4614
|
+
return next;
|
|
4615
|
+
}, (error) => {
|
|
4616
|
+
if (id === generation) pending = void 0;
|
|
4617
|
+
throw error;
|
|
4618
|
+
});
|
|
4619
|
+
promise.catch(() => {
|
|
4620
|
+
});
|
|
4621
|
+
pending = { generation: id, promise, frames };
|
|
4622
|
+
return promise;
|
|
4623
|
+
};
|
|
4624
|
+
const apply2 = (frame) => {
|
|
4625
|
+
if (frame.type === "sync") {
|
|
4626
|
+
state = void 0;
|
|
4627
|
+
void load();
|
|
4628
|
+
} else {
|
|
4629
|
+
if (state) state = applyGateFrame(state, frame);
|
|
4630
|
+
if (pending) bufferGateFrame(pending.frames, frame);
|
|
4631
|
+
}
|
|
4632
|
+
for (const listener of [...listeners]) listener(frame);
|
|
4633
|
+
};
|
|
4634
|
+
return {
|
|
4635
|
+
gateRuns: () => state ? Promise.resolve(state) : pending ? pending.promise : load(),
|
|
4636
|
+
followGates(listener) {
|
|
4637
|
+
listeners.add(listener);
|
|
4638
|
+
if (listeners.size === 1) stop = connect(apply2);
|
|
4639
|
+
return () => {
|
|
4640
|
+
if (!listeners.delete(listener) || listeners.size) return;
|
|
4641
|
+
stop?.();
|
|
4642
|
+
stop = void 0;
|
|
4643
|
+
state = void 0;
|
|
4644
|
+
pending = void 0;
|
|
4645
|
+
generation++;
|
|
4646
|
+
};
|
|
4647
|
+
}
|
|
4648
|
+
};
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
// src/pixie-gate-tail.tsx
|
|
4550
4652
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
4551
4653
|
var UNVERIFIED = /미확인|not verified|unverified|未确认/i;
|
|
4552
|
-
|
|
4553
|
-
|
|
4654
|
+
var atAnchor = (run, anchor) => anchor.kind === "turn" ? run.point === "turn-end" && run.turn === anchor.turn : anchor.kind === "tool" ? (run.point === "tool-before" || run.point === "tool-after") && run.tool?.callId === anchor.callId : run.point === "input" && run.messageId === anchor.messageId;
|
|
4655
|
+
function anchorRuns(runs, anchor) {
|
|
4656
|
+
const last = /* @__PURE__ */ new Map();
|
|
4657
|
+
for (const run of runs.filter((run2) => atAnchor(run2, anchor) && run2.showInChat !== false && (run2.status === "done" || run2.status === "error") && run2.result?.concludedBy !== "skipped").sort((a, b) => a.startedAt - b.startedAt)) last.set(run.point, run);
|
|
4658
|
+
return [...last.values()].sort((a, b) => a.startedAt - b.startedAt);
|
|
4659
|
+
}
|
|
4660
|
+
function tailContent(run) {
|
|
4661
|
+
const output = run.output;
|
|
4662
|
+
if (output?.note?.trim()) return { kind: "note", ...splitNote(output.note) };
|
|
4663
|
+
if (run.status === "error") return { kind: "error", reason: output?.reason ?? run.result?.forwardError?.reason ?? run.reason ?? "" };
|
|
4664
|
+
return {
|
|
4665
|
+
kind: "verdict",
|
|
4666
|
+
verdict: output?.verdict ?? "pass",
|
|
4667
|
+
...output?.reason?.trim() ? { reason: output.reason } : {},
|
|
4668
|
+
...output?.replace?.trim() ? { replace: output.replace } : {},
|
|
4669
|
+
...output?.append?.trim() ? { append: output.append } : {}
|
|
4670
|
+
};
|
|
4554
4671
|
}
|
|
4555
4672
|
function splitNote(note) {
|
|
4556
|
-
const
|
|
4557
|
-
return
|
|
4673
|
+
const match2 = /^\s*\[([^\]\n]{1,80})\]\s*\n?/.exec(note);
|
|
4674
|
+
return match2 ? { label: match2[1], body: note.slice(match2[0].length) } : { body: note };
|
|
4558
4675
|
}
|
|
4559
|
-
function
|
|
4560
|
-
const t = (key) => gateText(locale, key);
|
|
4676
|
+
function GateResults({ actions, anchor, locale, Surface = import_react15.default.Fragment }) {
|
|
4561
4677
|
const [runs, setRuns] = import_react15.default.useState([]);
|
|
4562
4678
|
const [disabled, setDisabled] = import_react15.default.useState([]);
|
|
4563
|
-
const [open, setOpen] = import_react15.default.useState(true);
|
|
4564
|
-
const [busy, setBusy] = import_react15.default.useState(false);
|
|
4565
|
-
const [error, setError] = import_react15.default.useState("");
|
|
4566
4679
|
import_react15.default.useEffect(() => {
|
|
4567
4680
|
let active = true;
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
}
|
|
4573
|
-
|
|
4574
|
-
|
|
4681
|
+
let refreshing;
|
|
4682
|
+
let latest = 0;
|
|
4683
|
+
const refresh = () => {
|
|
4684
|
+
const id = ++latest;
|
|
4685
|
+
const own2 = { id, frames: [] };
|
|
4686
|
+
refreshing = own2;
|
|
4687
|
+
actions.gateRuns().then((snapshot) => {
|
|
4688
|
+
if (!active || id !== latest) return;
|
|
4689
|
+
const record3 = own2.frames.reduce(applyGateFrame, snapshot);
|
|
4690
|
+
refreshing = void 0;
|
|
4691
|
+
setRuns(record3.runs);
|
|
4692
|
+
setDisabled(record3.disabledGateIds ?? []);
|
|
4693
|
+
}, () => {
|
|
4694
|
+
if (id === latest) refreshing = void 0;
|
|
4695
|
+
});
|
|
4696
|
+
};
|
|
4575
4697
|
const stop = actions.followGates((frame) => {
|
|
4576
4698
|
if (!active) return;
|
|
4577
4699
|
if (frame.type === "sync") {
|
|
4578
|
-
|
|
4700
|
+
refresh();
|
|
4579
4701
|
return;
|
|
4580
4702
|
}
|
|
4703
|
+
if (refreshing) bufferGateFrame(refreshing.frames, frame);
|
|
4581
4704
|
if (frame.type === "session") {
|
|
4582
4705
|
setDisabled(frame.disabledGateIds);
|
|
4583
4706
|
return;
|
|
4584
4707
|
}
|
|
4585
|
-
setRuns((current) => [...current.filter((
|
|
4708
|
+
setRuns((current) => [...current.filter((run) => run.id !== frame.run.id), frame.run]);
|
|
4586
4709
|
});
|
|
4587
|
-
|
|
4710
|
+
refresh();
|
|
4588
4711
|
return () => {
|
|
4589
4712
|
active = false;
|
|
4590
4713
|
stop();
|
|
4591
4714
|
};
|
|
4592
4715
|
}, [actions]);
|
|
4593
|
-
const
|
|
4594
|
-
if (!
|
|
4595
|
-
|
|
4596
|
-
|
|
4716
|
+
const shown = anchorRuns(runs, anchor);
|
|
4717
|
+
if (!shown.length) return null;
|
|
4718
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Surface, { children: shown.map((run) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(GateResultBlock, { run, actions, locale, off: disabled.includes(run.gateId), onDisabled: setDisabled }, run.id)) });
|
|
4719
|
+
}
|
|
4720
|
+
function GateTail({ turn, ...props }) {
|
|
4721
|
+
const anchor = import_react15.default.useMemo(() => ({ kind: "turn", turn }), [turn]);
|
|
4722
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(GateResults, { ...props, anchor });
|
|
4723
|
+
}
|
|
4724
|
+
function GateResultBlock({ run, actions, locale, off, onDisabled }) {
|
|
4725
|
+
const t = (key) => gateText(locale, key);
|
|
4726
|
+
const [open, setOpen] = import_react15.default.useState(true);
|
|
4727
|
+
const [busy, setBusy] = import_react15.default.useState(false);
|
|
4728
|
+
const [error, setError] = import_react15.default.useState("");
|
|
4729
|
+
const content = tailContent(run);
|
|
4730
|
+
const label = content.kind === "note" ? content.label : void 0;
|
|
4597
4731
|
const unverified = !!label && UNVERIFIED.test(label);
|
|
4598
4732
|
const turnOff = async () => {
|
|
4599
4733
|
setBusy(true);
|
|
4600
4734
|
setError("");
|
|
4601
4735
|
try {
|
|
4602
|
-
|
|
4736
|
+
onDisabled(await actions.setSessionGate(run.gateId, false));
|
|
4603
4737
|
} catch (cause) {
|
|
4604
4738
|
setError(`${t("sessionGateFailed")} (${cause instanceof Error ? cause.message : String(cause)})`);
|
|
4605
4739
|
} finally {
|
|
4606
4740
|
setBusy(false);
|
|
4607
4741
|
}
|
|
4608
4742
|
};
|
|
4609
|
-
return /* @__PURE__ */ (0, import_jsx_runtime17.
|
|
4743
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("section", { className: "studio-gate-tail-body", "aria-label": t("tailTitle").replace("{name}", run.gateName), "data-gate-tail": run.gateId, "data-gate-point": run.point, "data-tail-kind": content.kind, "data-unverified": unverified ? "true" : void 0, children: [
|
|
4610
4744
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "studio-gate-tail-head", children: [
|
|
4611
4745
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: label ?? run.gateName }),
|
|
4612
4746
|
unverified && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Badge, { tone: "warning", children: t("tailUnverified") }),
|
|
4613
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4747
|
+
content.kind === "verdict" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Badge, { tone: content.verdict === "stop" ? "warning" : "success", children: t(content.verdict) }),
|
|
4748
|
+
content.kind === "error" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Badge, { tone: "warning", children: t("error") }),
|
|
4749
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "studio-card-meta", children: `${t("tailTitle").replace("{name}", run.gateName)} \xB7 ${gatePointText(locale, run.point)}` }),
|
|
4614
4750
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "studio-inline-actions", children: [
|
|
4615
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { small: true, onClick: () => setOpen((value) => !value), children: t(open ? "tailHide" : "tailShow") }),
|
|
4751
|
+
content.kind !== "error" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { small: true, onClick: () => setOpen((value) => !value), children: t(open ? "tailHide" : "tailShow") }),
|
|
4616
4752
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { small: true, onClick: actions.openTab, children: t("tailOpen") }),
|
|
4617
4753
|
!off && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { small: true, disabled: busy, onClick: () => {
|
|
4618
4754
|
void turnOff();
|
|
@@ -4621,8 +4757,14 @@ function GateTail({ actions, turn, locale, Surface = import_react15.default.Frag
|
|
|
4621
4757
|
] }),
|
|
4622
4758
|
error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Notice, { children: error }),
|
|
4623
4759
|
off && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { role: "status", className: "studio-muted", children: t("tailOffDone") }),
|
|
4624
|
-
|
|
4625
|
-
|
|
4760
|
+
content.kind === "error" && content.reason && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "studio-muted", children: content.reason }),
|
|
4761
|
+
open && content.kind === "note" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModelText, { text: content.body, locale }),
|
|
4762
|
+
open && content.kind === "verdict" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
4763
|
+
content.reason && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModelText, { text: content.reason, locale }),
|
|
4764
|
+
content.replace && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Disclosure, { title: t("replace"), children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModelText, { text: content.replace, locale }) }),
|
|
4765
|
+
content.append && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Disclosure, { title: t("append"), children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModelText, { text: content.append, locale }) })
|
|
4766
|
+
] })
|
|
4767
|
+
] });
|
|
4626
4768
|
}
|
|
4627
4769
|
|
|
4628
4770
|
// src/pixie-settings.tsx
|
|
@@ -4645,6 +4787,7 @@ var GATE_GUIDE_URL = "https://github.com/matamt-dbg/dsh-studio/blob/main/docs/ap
|
|
|
4645
4787
|
var enabledFor = (task, preset) => task.enabled && task.presetIds.includes(preset) && !task.disabledPresetIds?.includes(preset);
|
|
4646
4788
|
var isTool = (when) => when === "tool-before" || when === "tool-after";
|
|
4647
4789
|
var isMessage = (when) => when === "user-message" || when === "agent-message";
|
|
4790
|
+
var showsInChat = (task) => task.advanced.showInChat !== false;
|
|
4648
4791
|
function makeTask(name, when = "user-message", instruction = "") {
|
|
4649
4792
|
return {
|
|
4650
4793
|
id: globalThis.crypto.randomUUID(),
|
|
@@ -4652,7 +4795,7 @@ function makeTask(name, when = "user-message", instruction = "") {
|
|
|
4652
4795
|
enabled: true,
|
|
4653
4796
|
when,
|
|
4654
4797
|
instruction,
|
|
4655
|
-
advanced: { confirmReplace: false, onError: "pass", agents: when === "agent-message" ? "child" : "root", timeoutMs: 6e4, maxSteers: 1 },
|
|
4798
|
+
advanced: { confirmReplace: false, onError: "pass", agents: when === "agent-message" ? "child" : "root", timeoutMs: 6e4, maxSteers: 1, showInChat: true },
|
|
4656
4799
|
presetIds: []
|
|
4657
4800
|
};
|
|
4658
4801
|
}
|
|
@@ -4888,6 +5031,7 @@ function GateTaskEditor({ task: initial2, open, locale, actions, onApply, onClos
|
|
|
4888
5031
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "studio-muted", children: t("scriptEnv") }),
|
|
4889
5032
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "studio-muted", children: withLink(t("scriptShell"), /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("a", { href: GATE_GUIDE_URL, target: "_blank", rel: "noreferrer", children: t("guideLink") })) })
|
|
4890
5033
|
] }),
|
|
5034
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SettingsRow, { title: t("showInChat"), description: t("showInChatHint"), control: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Toggle, { label: t("showInChat"), checked: showsInChat(task), onChange: (showInChat) => adv({ showInChat }) }) }),
|
|
4891
5035
|
isMessage(task.when) && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SettingsRow, { title: t("confirmReplace"), description: t("confirmReplaceHint"), control: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Toggle, { label: t("confirmReplace"), checked: task.advanced.confirmReplace, onChange: (confirmReplace) => adv({ confirmReplace }) }) }),
|
|
4892
5036
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SettingsRow, { title: t("onError"), control: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Segmented, { label: t("onError"), value: task.advanced.onError, options: [{ value: "pass", label: t("errorPass") }, { value: "block", label: t("errorBlock") }], onChange: (onError) => adv({ onError }) }) }),
|
|
4893
5037
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SettingsRow, { title: t("agents"), control: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Segmented, { label: t("agents"), value: task.advanced.agents, options: ["root", "child", "any"].map((value) => ({ value, label: t(value) })), onChange: (agents) => adv({ agents }) }) }),
|
|
@@ -5236,7 +5380,7 @@ var messages3 = {
|
|
|
5236
5380
|
};
|
|
5237
5381
|
function gateComposeText(locale, key, values = {}) {
|
|
5238
5382
|
const text = messages3[key][locale.startsWith("ko") ? 0 : locale.startsWith("zh") ? 2 : 1];
|
|
5239
|
-
return text.replace(/\{(\w+)\}/g, (
|
|
5383
|
+
return text.replace(/\{(\w+)\}/g, (match2, name) => name in values ? String(values[name]) : match2);
|
|
5240
5384
|
}
|
|
5241
5385
|
|
|
5242
5386
|
// src/pixie-settings.tsx
|
|
@@ -5656,16 +5800,34 @@ function mountPixie(ctx, options = {}) {
|
|
|
5656
5800
|
function GateTailSurface({ children }) {
|
|
5657
5801
|
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StudioSurface, { className: "studio-gate-tail", children });
|
|
5658
5802
|
}
|
|
5803
|
+
const hubs = /* @__PURE__ */ new Map();
|
|
5804
|
+
const resultActions = (sessionId) => {
|
|
5805
|
+
let hub = hubs.get(sessionId);
|
|
5806
|
+
if (!hub) {
|
|
5807
|
+
const tab = tabActions(sessionId);
|
|
5808
|
+
hub = gateRunHub(tab.gateRuns, tab.followGates);
|
|
5809
|
+
hubs.set(sessionId, hub);
|
|
5810
|
+
}
|
|
5811
|
+
return { ...hub, setSessionGate: (gateId, enabled) => unwrap(remote().setSessionGate(sessionId, gateId, enabled)), openTab };
|
|
5812
|
+
};
|
|
5659
5813
|
function TurnTail({ sessionId, turn }) {
|
|
5660
5814
|
const active = useLocale();
|
|
5661
|
-
const actions = import_react18.default.useMemo(() =>
|
|
5662
|
-
const tab = tabActions(sessionId);
|
|
5663
|
-
return { gateRuns: tab.gateRuns, followGates: tab.followGates, setSessionGate: (gateId, enabled) => unwrap(remote().setSessionGate(sessionId, gateId, enabled)), openTab };
|
|
5664
|
-
}, [sessionId]);
|
|
5815
|
+
const actions = import_react18.default.useMemo(() => resultActions(sessionId), [sessionId]);
|
|
5665
5816
|
if (turn.status !== "closed") return null;
|
|
5666
5817
|
return withMarkdown(/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(GateTail, { actions, turn: turn.turn, locale: active, Surface: GateTailSurface }));
|
|
5667
5818
|
}
|
|
5668
5819
|
scope.slots.inject("conversation.chat.turnTail", () => scope.slots.register({ name: "conversation.chat.turnTail", id: "studio-pixie-gate-tail" }, TurnTail));
|
|
5820
|
+
function GateResultRow({ sessionId, node }) {
|
|
5821
|
+
const active = useLocale();
|
|
5822
|
+
const actions = import_react18.default.useMemo(() => resultActions(sessionId), [sessionId]);
|
|
5823
|
+
const anchor = node.data.anchor;
|
|
5824
|
+
return withMarkdown(/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(GateResults, { actions, anchor, locale: active, Surface: GateTailSurface }));
|
|
5825
|
+
}
|
|
5826
|
+
scope.inject(["uiConversation"], (inner) => {
|
|
5827
|
+
const conversation = inner.uiConversation;
|
|
5828
|
+
conversation.events.register(gateResultDefinition);
|
|
5829
|
+
inner.slots.inject("conversation.chat.node", () => inner.slots.register({ name: "conversation.chat.node", key: GATE_RESULT_NODE }, GateResultRow));
|
|
5830
|
+
});
|
|
5669
5831
|
});
|
|
5670
5832
|
}
|
|
5671
5833
|
|
package/index.js
CHANGED
|
@@ -57,7 +57,7 @@ var JUDGE_PLUGIN = `${PACKAGE_NAME}/judge`;
|
|
|
57
57
|
function currentPluginName(name) {
|
|
58
58
|
return name === LEGACY_PACKAGE_NAME || name.startsWith(`${LEGACY_PACKAGE_NAME}/`) ? PACKAGE_NAME + name.slice(LEGACY_PACKAGE_NAME.length) : name;
|
|
59
59
|
}
|
|
60
|
-
var STUDIO_VERSION = "0.
|
|
60
|
+
var STUDIO_VERSION = "0.5.0";
|
|
61
61
|
|
|
62
62
|
// src/delegation.ts
|
|
63
63
|
var ROLE_TOOL = "subagent_role";
|
|
@@ -1624,7 +1624,7 @@ var text = (value) => typeof value === "string" && !!value.trim();
|
|
|
1624
1624
|
function validateGateTask(value) {
|
|
1625
1625
|
if (!object3(value) || !text(value.id) || !text(value.name) || typeof value.enabled !== "boolean" || !GATE_WHENS.includes(value.when) || typeof value.instruction !== "string") throw new Error("Invalid gate task");
|
|
1626
1626
|
const advanced = value.advanced;
|
|
1627
|
-
if (!object3(advanced) || typeof advanced.confirmReplace !== "boolean" || !["pass", "block"].includes(advanced.onError) || !["root", "child", "any"].includes(advanced.agents) || typeof advanced.timeoutMs !== "number" || !Number.isFinite(advanced.timeoutMs) || typeof advanced.maxSteers !== "number") throw new Error("Invalid gate advanced options");
|
|
1627
|
+
if (!object3(advanced) || typeof advanced.confirmReplace !== "boolean" || !["pass", "block"].includes(advanced.onError) || !["root", "child", "any"].includes(advanced.agents) || typeof advanced.timeoutMs !== "number" || !Number.isFinite(advanced.timeoutMs) || typeof advanced.maxSteers !== "number" || advanced.showInChat !== void 0 && typeof advanced.showInChat !== "boolean") throw new Error("Invalid gate advanced options");
|
|
1628
1628
|
if (advanced.script !== void 0 && (typeof advanced.script !== "string" || (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(advanced.script) || advanced.script.includes("..")))) throw new Error("Invalid gate script filename");
|
|
1629
1629
|
if (!Array.isArray(value.presetIds) || !value.presetIds.every(text)) throw new Error("Invalid gate presetIds");
|
|
1630
1630
|
if (value.disabledPresetIds !== void 0 && (!Array.isArray(value.disabledPresetIds) || !value.disabledPresetIds.every(text))) throw new Error("Invalid gate disabledPresetIds");
|
|
@@ -1976,6 +1976,7 @@ var PixieGateStore = class {
|
|
|
1976
1976
|
point: snapshot.point,
|
|
1977
1977
|
...snapshot.when ? { when: snapshot.when } : {},
|
|
1978
1978
|
sessionId,
|
|
1979
|
+
...snapshot.advanced ? { showInChat: snapshot.advanced.showInChat !== false } : {},
|
|
1979
1980
|
startedAt: Date.now(),
|
|
1980
1981
|
original,
|
|
1981
1982
|
finalText: original,
|
|
@@ -2012,6 +2013,7 @@ var PixieGateStore = class {
|
|
|
2012
2013
|
if (value.reason !== void 0) run.reason = value.reason;
|
|
2013
2014
|
if (value.turn !== void 0) run.turn = value.turn;
|
|
2014
2015
|
if (value.tool !== void 0) run.tool = value.tool;
|
|
2016
|
+
if (value.messageId !== void 0) run.messageId = value.messageId;
|
|
2015
2017
|
if (value.steerCount !== void 0) run.steerCount = value.steerCount;
|
|
2016
2018
|
if (value.result) {
|
|
2017
2019
|
run.result = value.result;
|
|
@@ -2717,7 +2719,7 @@ function gateInput(deps) {
|
|
|
2717
2719
|
deps.emit("pixie-gate/start", structuredClone(run));
|
|
2718
2720
|
const execution = await executeTask(gate, original, payload.signal, deps.runner, () => deps.hookContext?.(payload.agent, payload.turn) ?? { sessionId: payload.agent.id, cwd: payload.agent.session.header.cwd, turn: payload.turn, agent: root ? "root" : "child" });
|
|
2719
2721
|
const { result, plan } = execution;
|
|
2720
|
-
const item = { run, execution, result, applied: execution.skipped ? "none" : "pass" };
|
|
2722
|
+
const item = { messageId: String(message.id), run, execution, result, applied: execution.skipped ? "none" : "pass" };
|
|
2721
2723
|
pending.push(item);
|
|
2722
2724
|
if (payload.signal.aborted || result.verdict === "cancelled") break;
|
|
2723
2725
|
const suggestion = plan.suggestion;
|
|
@@ -2753,9 +2755,9 @@ function gateInput(deps) {
|
|
|
2753
2755
|
addition: !cancelled && !rejected && item.execution.plan.addition !== void 0,
|
|
2754
2756
|
withheld: !cancelled && rejected
|
|
2755
2757
|
});
|
|
2756
|
-
await deps.recorder.finish(payload.agent.id, item.run.id, { status: cancelled ? "cancelled" : item.result.verdict === "error" ? "error" : "done", result: item.result, output: item.execution.output, usage: item.execution.usage, applied, hooksInput, ...applied === "rejected" ? { rejection: item.rejection } : {} });
|
|
2758
|
+
await deps.recorder.finish(payload.agent.id, item.run.id, { status: cancelled ? "cancelled" : item.result.verdict === "error" ? "error" : "done", result: item.result, output: item.execution.output, usage: item.execution.usage, applied, hooksInput, messageId: item.messageId, ...applied === "rejected" ? { rejection: item.rejection } : {} });
|
|
2757
2759
|
const usage = item.execution.usage;
|
|
2758
|
-
deps.emit("pixie-gate/end", { ...item.run, result: item.result, output: item.execution.output, finalText: item.result.subject, applied, hooksInput, usage, endedAt: Date.now(), status: cancelled ? "cancelled" : item.result.verdict === "error" ? "error" : "done", ...applied === "rejected" && item.rejection ? { rejection: { ...item.rejection, original: item.run.original, status: "pending" } } : {} });
|
|
2760
|
+
deps.emit("pixie-gate/end", { ...item.run, messageId: item.messageId, result: item.result, output: item.execution.output, finalText: item.result.subject, applied, hooksInput, usage, endedAt: Date.now(), status: cancelled ? "cancelled" : item.result.verdict === "error" ? "error" : "done", ...applied === "rejected" && item.rejection ? { rejection: { ...item.rejection, original: item.run.original, status: "pending" } } : {} });
|
|
2759
2761
|
}
|
|
2760
2762
|
return committed;
|
|
2761
2763
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matamt-dbg/dsh-studio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "DSH Studio: build, edit and apply DeepSeek Harness agent presets — tools, prompts, Configs and role-based subagents — from the web GUI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -32,7 +32,7 @@ Help the user build a Pixie Gate: a task the Pixie model runs at one point of an
|
|
|
32
32
|
- Write the whole judgment in `--question`: question IDs never reach the model. For several inputs (source and translation, answer and evidence), pipe a JSON object built with `jq` and name its fields in backticks in the question.
|
|
33
33
|
- The script runs in the session working directory under the session sandbox. Do not write files unless the session can write; never try to escape the sandbox.
|
|
34
34
|
- In the task, reference only `{{hooks.<name>.text}}` and `{{hooks.<name>.data}}` of the chosen script.
|
|
35
|
-
- At `
|
|
35
|
+
- At every point, the gate's **Show the result in the chat** setting (`advanced.showInChat`, on by default) decides whether the result shows right under what it checked (the message, the tool call, or the turn's answer), whoever concluded it: the note if any, otherwise the verdict and reason. It is never written to the session log. For a note, start it with a bracketed label line (e.g. `[한국어 번역]`) to title the block; a label containing `not verified`/`미확인` gets a warning badge.
|
|
36
36
|
|
|
37
37
|
Test a script by hand before relying on it: `printf 'sample' | PIXIE_POINT=tool-after PIXIE_TOOL_NAME=grep ./script.sh; echo $?`. The helper only works inside a real gate run.
|
|
38
38
|
|
|
@@ -42,7 +42,7 @@ Do not edit `definitions.json` unless the user asks you to; prefer guiding them
|
|
|
42
42
|
|
|
43
43
|
```json
|
|
44
44
|
{ "id": "<uuid>", "name": "…", "enabled": true, "when": "user-message", "instruction": "…",
|
|
45
|
-
"advanced": { "confirmReplace": false, "onError": "pass", "agents": "root", "timeoutMs": 60000, "maxSteers": 1, "script": "optional.sh" },
|
|
45
|
+
"advanced": { "confirmReplace": false, "onError": "pass", "agents": "root", "timeoutMs": 60000, "maxSteers": 1, "showInChat": true, "script": "optional.sh" },
|
|
46
46
|
"presetIds": ["<preset id>"], "disabledPresetIds": ["<attached preset where it is off>"] }
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -56,11 +56,12 @@ When the verdict is stop, the replacement and addition never reach the main mode
|
|
|
56
56
|
| Option | Default | Description |
|
|
57
57
|
| --- | --- | --- |
|
|
58
58
|
| Pixie hook script | None | A script run before the task. See [below](#pixie-hook-scripts) |
|
|
59
|
+
| Show the result in the chat | On | Shows the result right under what the gate checked. See [Chat notes](#chat-notes-and-per-session-switches) |
|
|
59
60
|
| Confirm before replacing | Off | Stops a real replacement and shows it as a suggestion (message points) |
|
|
60
|
-
|
|
|
61
|
-
| Target agents |
|
|
62
|
-
| Time limit | 60
|
|
63
|
-
| Redo limit | 1 | Corrections per turn at the answer-end point |
|
|
61
|
+
| If Pixie fails | Continue as usual | Continue as usual or stop it on model or script errors and timeouts |
|
|
62
|
+
| Target agents | Main agent (Subagents for agent messages) | Main agent, Subagents, or All |
|
|
63
|
+
| Time limit (seconds) | 60 | Script, helper, and model calls together |
|
|
64
|
+
| Redo limit per turn | 1 | Corrections per turn at the answer-end point |
|
|
64
65
|
|
|
65
66
|
## Pixie hook scripts
|
|
66
67
|
|
|
@@ -236,12 +237,31 @@ On start, Studio installs a skill with this guide and the hook rules into `<DSH
|
|
|
236
237
|
|
|
237
238
|
## Chat notes and per-session switches
|
|
238
239
|
|
|
239
|
-
**Chat notes:**
|
|
240
|
+
**Chat notes:** a gate with **Show the result in the chat** on (Advanced; on by default) shows its result as a block right under what it checked, whether the script (exit 11) or Pixie concluded it. The setting decides; the output does not. The block is drawn from Studio's gate records and is never written to the session log.
|
|
240
241
|
|
|
242
|
+
| Point | Where the block appears |
|
|
243
|
+
| --- | --- |
|
|
244
|
+
| You send an instruction / agents message each other | Right under that message, outside the turn's folded steps. A subagent's message shows in the subagent's chat |
|
|
245
|
+
| Before a tool runs / a tool result arrives | Right under that tool call, inside the turn's steps (expand the turn to see it). Before and after results of one call show together |
|
|
246
|
+
| The agent finishes its answer | Right under that turn's answer |
|
|
247
|
+
|
|
248
|
+
A rejected instruction has no message in the chat, so its result stays in the Pixie tab only.
|
|
249
|
+
|
|
250
|
+

|
|
251
|
+
|
|
252
|
+
When the turn is folded, the instruction and answer results stay visible; tool results fold with the steps.
|
|
253
|
+
|
|
254
|
+

|
|
255
|
+
|
|
256
|
+
Turn the setting off in the gate editor under **Advanced → Show the result in the chat**. The gate still runs and its record stays in the Pixie tab.
|
|
257
|
+
|
|
258
|
+

|
|
259
|
+
|
|
260
|
+
- The block shows the note when there is one, for example a translation. Without a note it shows the verdict with its reason, and any replacement or addition in a collapsed section. A failed run shows one error line.
|
|
241
261
|
- If the note's first line starts with a bracketed label such as `[한국어 번역]`, the label becomes the block title. A label containing `not verified` (or `미확인`) gets a **not verified** badge.
|
|
242
262
|
- The body renders as Markdown. The block offers **Hide**, **Open in Pixie tab**, and **Turn off in this session**.
|
|
243
|
-
- It appears after the
|
|
244
|
-
- The
|
|
263
|
+
- It appears after the gate finishes; there is no progress indicator. Skipped turns (exit 10), cancelled runs, and runs whose gate had the setting off get no block. Each run keeps the setting it started with. If a gate ran more than once at the same place, only the last result shows.
|
|
264
|
+
- The result block does not change the checked message, tool call, or answer. **Hide** collapses only the gate result.
|
|
245
265
|
|
|
246
266
|
**Per-session switches:** at the top of **Pixie** tab → **Gates**, **Gates in this session** lists every gate that applies to this session's preset with a switch that turns it off for this session only. **Turn off in this session** on a chat block is the same switch.
|
|
247
267
|
|