@clwnd/opencode 0.11.0 → 0.12.1
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/index.js +132 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,7 +14,6 @@ import { join } from "path";
|
|
|
14
14
|
var DEFAULTS = {
|
|
15
15
|
maxProcs: 4,
|
|
16
16
|
idleTimeout: 3e4,
|
|
17
|
-
ocCompaction: false,
|
|
18
17
|
smallModel: "",
|
|
19
18
|
permissionDusk: 6e4,
|
|
20
19
|
droned: false,
|
|
@@ -32,7 +31,6 @@ function loadConfig() {
|
|
|
32
31
|
cached = {
|
|
33
32
|
maxProcs: raw.maxProcs ?? DEFAULTS.maxProcs,
|
|
34
33
|
idleTimeout: raw.idleTimeout ?? DEFAULTS.idleTimeout,
|
|
35
|
-
ocCompaction: raw.ocCompaction ?? DEFAULTS.ocCompaction,
|
|
36
34
|
smallModel: raw.smallModel ?? DEFAULTS.smallModel,
|
|
37
35
|
permissionDusk: raw.permissionDusk ?? DEFAULTS.permissionDusk,
|
|
38
36
|
droned: raw.droned ?? DEFAULTS.droned,
|
|
@@ -381,6 +379,32 @@ function mapToolName(name) {
|
|
|
381
379
|
return TOOL_NAME_MAP[name] ?? name;
|
|
382
380
|
}
|
|
383
381
|
var BROKERED_TOOLS = /* @__PURE__ */ new Set(["webfetch", "websearch", "todowrite"]);
|
|
382
|
+
var KNOWN_TOOLS = /* @__PURE__ */ new Set([
|
|
383
|
+
"read",
|
|
384
|
+
"edit",
|
|
385
|
+
"write",
|
|
386
|
+
"bash",
|
|
387
|
+
"glob",
|
|
388
|
+
"grep",
|
|
389
|
+
"webfetch",
|
|
390
|
+
"websearch",
|
|
391
|
+
"todowrite",
|
|
392
|
+
"task",
|
|
393
|
+
"skill",
|
|
394
|
+
"todoread",
|
|
395
|
+
"taskoutput",
|
|
396
|
+
"taskstop",
|
|
397
|
+
"question",
|
|
398
|
+
"clwnd_permission",
|
|
399
|
+
"permission_prompt",
|
|
400
|
+
"cronCreate",
|
|
401
|
+
"cronDelete",
|
|
402
|
+
"cronList",
|
|
403
|
+
"notebookedit",
|
|
404
|
+
"codesearch",
|
|
405
|
+
"applypatch",
|
|
406
|
+
"ls"
|
|
407
|
+
]);
|
|
384
408
|
var INPUT_FIELD_MAP = {
|
|
385
409
|
read: { file_path: "filePath" },
|
|
386
410
|
edit: { file_path: "filePath", old_string: "oldString", new_string: "newString", replace_all: "replaceAll" },
|
|
@@ -652,6 +676,7 @@ function isBrokeredToolReturn(prompt) {
|
|
|
652
676
|
return false;
|
|
653
677
|
}
|
|
654
678
|
var sessionLastAgent = /* @__PURE__ */ new Map();
|
|
679
|
+
var sessionPetalCounts = /* @__PURE__ */ new Map();
|
|
655
680
|
function detectAgent(sid, headers) {
|
|
656
681
|
const raw = headers?.["x-clwnd-agent"] ?? null;
|
|
657
682
|
if (!raw) return null;
|
|
@@ -694,6 +719,27 @@ async function getSessionPermissions(client, sessionId) {
|
|
|
694
719
|
return [];
|
|
695
720
|
}
|
|
696
721
|
}
|
|
722
|
+
var mcpConfigCache = null;
|
|
723
|
+
async function getMcpServerConfigs(client) {
|
|
724
|
+
if (mcpConfigCache) return mcpConfigCache;
|
|
725
|
+
if (!client) return [];
|
|
726
|
+
try {
|
|
727
|
+
const resp = await client.config.get();
|
|
728
|
+
const mcp = resp.data?.mcp;
|
|
729
|
+
if (!mcp) return [];
|
|
730
|
+
const configs = [];
|
|
731
|
+
for (const [name, cfg] of Object.entries(mcp)) {
|
|
732
|
+
if (cfg.type === "local" && Array.isArray(cfg.command)) {
|
|
733
|
+
configs.push({ name, type: "local", command: cfg.command, environment: cfg.environment });
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
mcpConfigCache = configs;
|
|
737
|
+
if (configs.length > 0) trace("mcp.configs.loaded", { servers: configs.map((c) => c.name).join(",") });
|
|
738
|
+
return configs;
|
|
739
|
+
} catch {
|
|
740
|
+
return [];
|
|
741
|
+
}
|
|
742
|
+
}
|
|
697
743
|
var lastAllowedTools = /* @__PURE__ */ new Map();
|
|
698
744
|
var AGENT_DENY = {
|
|
699
745
|
plan: /* @__PURE__ */ new Set(["edit", "write"])
|
|
@@ -768,6 +814,8 @@ var ClwndModel = class {
|
|
|
768
814
|
}
|
|
769
815
|
async doStream(opts) {
|
|
770
816
|
const sid = opts.headers?.["x-opencode-session"] ?? sigil(Date.now().toString());
|
|
817
|
+
const lastRole = opts.prompt.length > 0 ? opts.prompt[opts.prompt.length - 1].role : "none";
|
|
818
|
+
trace("doStream.enter", { sid, promptLen: opts.prompt.length, lastRole });
|
|
771
819
|
const content = extractContent(opts.prompt, sid);
|
|
772
820
|
const text = content.filter((p) => p.type === "text").map((p) => p.text).join("\n\n");
|
|
773
821
|
const systemPrompt = extractSystemPrompt(opts.prompt);
|
|
@@ -777,8 +825,30 @@ var ClwndModel = class {
|
|
|
777
825
|
const sap = /* @__PURE__ */ new Map();
|
|
778
826
|
const permissions = await getSessionPermissions(this.config.client, sid);
|
|
779
827
|
const allowedTools = deriveAllowedTools(sid, opts);
|
|
780
|
-
|
|
781
|
-
|
|
828
|
+
const isAux = isAuxiliaryCall(opts);
|
|
829
|
+
if (isAux) trace("auxiliary.passthrough", { method: "doStream", sid });
|
|
830
|
+
let permAskId = null;
|
|
831
|
+
const isPermReturn = isBrokeredToolReturn(opts.prompt) && (() => {
|
|
832
|
+
const lt = opts.prompt.findLast((m) => m.role === "tool");
|
|
833
|
+
if (!lt || !Array.isArray(lt.content)) return false;
|
|
834
|
+
for (const p of lt.content) {
|
|
835
|
+
if (p.type === "tool-result" && p.toolCallId?.startsWith("perm-")) {
|
|
836
|
+
const rawOutput = p.output ?? p.result;
|
|
837
|
+
try {
|
|
838
|
+
const outer = typeof rawOutput === "string" ? JSON.parse(rawOutput) : rawOutput;
|
|
839
|
+
const inner = outer?.value ?? outer;
|
|
840
|
+
const str = typeof inner === "string" ? inner : JSON.stringify(inner ?? "");
|
|
841
|
+
const parsed = JSON.parse(str);
|
|
842
|
+
if (parsed.askId) permAskId = parsed.askId;
|
|
843
|
+
} catch {
|
|
844
|
+
}
|
|
845
|
+
return true;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return false;
|
|
849
|
+
})();
|
|
850
|
+
if (isBrokeredToolReturn(opts.prompt) && !isPermReturn) {
|
|
851
|
+
trace("brokered.return", { sid });
|
|
782
852
|
return {
|
|
783
853
|
stream: new ReadableStream({
|
|
784
854
|
start(controller) {
|
|
@@ -788,36 +858,47 @@ var ClwndModel = class {
|
|
|
788
858
|
})
|
|
789
859
|
};
|
|
790
860
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
}
|
|
861
|
+
const listenOnly = !!isPermReturn;
|
|
862
|
+
const priorPetals = opts.prompt.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "tool");
|
|
863
|
+
trace("priorPetals", { sid, count: priorPetals.length, roles: priorPetals.map((m) => m.role).join(",") });
|
|
864
|
+
if (!isAux) {
|
|
865
|
+
const prev = sessionPetalCounts.get(sid) ?? 0;
|
|
866
|
+
sessionPetalCounts.set(sid, priorPetals.length);
|
|
867
|
+
if (prev > 0 && priorPetals.length < prev * 0.5) {
|
|
868
|
+
trace("compaction.detected", { sid, prev, now: priorPetals.length });
|
|
869
|
+
hum({ chi: "cancel", sid, reason: "compaction" });
|
|
801
870
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
};
|
|
871
|
+
}
|
|
872
|
+
const externalTools = [];
|
|
873
|
+
const externalToolNames = /* @__PURE__ */ new Set();
|
|
874
|
+
if (opts.tools) {
|
|
875
|
+
for (const t of opts.tools) {
|
|
876
|
+
if (t.type !== "function") continue;
|
|
877
|
+
const name = t.name;
|
|
878
|
+
if (KNOWN_TOOLS.has(name)) continue;
|
|
879
|
+
externalTools.push({ name, description: t.description, inputSchema: t.inputSchema });
|
|
880
|
+
externalToolNames.add(name);
|
|
811
881
|
}
|
|
812
882
|
}
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
})();
|
|
817
|
-
const priorPetals = opts.prompt.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "tool");
|
|
818
|
-
trace("priorPetals", { sid, count: priorPetals.length, roles: priorPetals.map((m) => m.role).join(",") });
|
|
883
|
+
const allToolNames = opts.tools ? opts.tools.filter((t) => t.type === "function").map((t) => t.name) : [];
|
|
884
|
+
trace("tools.available", { sid, count: allToolNames.length, names: allToolNames.join(",") });
|
|
885
|
+
if (externalTools.length > 0) trace("external.tools.detected", { sid, names: [...externalToolNames].join(",") });
|
|
819
886
|
let promptSent = false;
|
|
820
|
-
if (
|
|
887
|
+
if (listenOnly && humAlive) {
|
|
888
|
+
hum({
|
|
889
|
+
chi: "prompt",
|
|
890
|
+
sid,
|
|
891
|
+
cwd,
|
|
892
|
+
modelId: self.modelId,
|
|
893
|
+
listenOnly: true,
|
|
894
|
+
dusk: duskIn(3e4)
|
|
895
|
+
});
|
|
896
|
+
promptSent = true;
|
|
897
|
+
if (permAskId) {
|
|
898
|
+
trace("permission.hold.releasing", { sid, askId: permAskId });
|
|
899
|
+
hum({ chi: "release-permit", askId: permAskId, decision: "allow" });
|
|
900
|
+
}
|
|
901
|
+
} else if (!listenOnly && humAlive) {
|
|
821
902
|
hum({
|
|
822
903
|
chi: "prompt",
|
|
823
904
|
sid,
|
|
@@ -829,8 +910,13 @@ var ClwndModel = class {
|
|
|
829
910
|
permissions,
|
|
830
911
|
allowedTools,
|
|
831
912
|
listenOnly,
|
|
913
|
+
auxiliary: isAux || void 0,
|
|
914
|
+
// skip graft for compaction/title gen
|
|
832
915
|
ocServerUrl: self.config.pluginInput?.serverUrl?.toString(),
|
|
833
916
|
priorPetals,
|
|
917
|
+
externalTools: externalTools.length > 0 ? externalTools : void 0,
|
|
918
|
+
mcpServerConfigs: await getMcpServerConfigs(this.config.client),
|
|
919
|
+
visibleTools: allToolNames,
|
|
834
920
|
dusk: duskIn(3e4)
|
|
835
921
|
});
|
|
836
922
|
promptSent = true;
|
|
@@ -839,6 +925,7 @@ var ClwndModel = class {
|
|
|
839
925
|
async start(controller) {
|
|
840
926
|
let done = false;
|
|
841
927
|
const tendrils = /* @__PURE__ */ new Set();
|
|
928
|
+
const streamBrokered = new Set(BROKERED_TOOLS);
|
|
842
929
|
const buds = [];
|
|
843
930
|
const metaQueue = [];
|
|
844
931
|
let textId = "t0";
|
|
@@ -908,6 +995,7 @@ var ClwndModel = class {
|
|
|
908
995
|
const ct = raw.chunkType;
|
|
909
996
|
if (ct === "text_start" || ct === "text_delta" && !textStarted) {
|
|
910
997
|
if (!textStarted) {
|
|
998
|
+
textId = `t${Date.now()}`;
|
|
911
999
|
textStarted = true;
|
|
912
1000
|
petal({ type: "text-start", id: textId });
|
|
913
1001
|
}
|
|
@@ -917,6 +1005,7 @@ var ClwndModel = class {
|
|
|
917
1005
|
}
|
|
918
1006
|
if (ct === "reasoning_start" || ct === "reasoning_delta" && !reasoningStarted) {
|
|
919
1007
|
if (!reasoningStarted) {
|
|
1008
|
+
reasoningId = `r${Date.now()}`;
|
|
920
1009
|
reasoningStarted = true;
|
|
921
1010
|
petal({ type: "reasoning-start", id: reasoningId });
|
|
922
1011
|
}
|
|
@@ -929,6 +1018,14 @@ var ClwndModel = class {
|
|
|
929
1018
|
reasoningStarted = false;
|
|
930
1019
|
}
|
|
931
1020
|
if (ct === "tool_input_start" && raw.toolCallId && raw.toolName) {
|
|
1021
|
+
if (textStarted) {
|
|
1022
|
+
petal({ type: "text-end", id: textId });
|
|
1023
|
+
textStarted = false;
|
|
1024
|
+
}
|
|
1025
|
+
if (reasoningStarted) {
|
|
1026
|
+
petal({ type: "reasoning-end", id: reasoningId });
|
|
1027
|
+
reasoningStarted = false;
|
|
1028
|
+
}
|
|
932
1029
|
sap.set(raw.toolCallId, "");
|
|
933
1030
|
buds.push({ type: "tool-input-start", id: raw.toolCallId, toolName: mapToolName(raw.toolName) });
|
|
934
1031
|
}
|
|
@@ -951,7 +1048,7 @@ var ClwndModel = class {
|
|
|
951
1048
|
} else {
|
|
952
1049
|
rawInput = "{}";
|
|
953
1050
|
}
|
|
954
|
-
const isBrokered =
|
|
1051
|
+
const isBrokered = streamBrokered.has(ocToolName);
|
|
955
1052
|
if (isBrokered) tendrils.add(raw.toolCallId);
|
|
956
1053
|
buds.push({
|
|
957
1054
|
type: "tool-call",
|
|
@@ -1177,18 +1274,17 @@ var clwndPlugin = async (input) => {
|
|
|
1177
1274
|
},
|
|
1178
1275
|
async execute(args, ctx) {
|
|
1179
1276
|
trace("permission.tool.invoked", { tool: args.tool, path: args.path });
|
|
1277
|
+
const t0 = Date.now();
|
|
1180
1278
|
await ctx.ask({
|
|
1181
1279
|
permission: args.tool === "edit" || args.tool === "write" ? "edit" : args.tool,
|
|
1182
1280
|
patterns: [args.path ?? "*"],
|
|
1183
1281
|
metadata: { tool: args.tool, filepath: args.path },
|
|
1184
1282
|
always: [args.path ?? "*"]
|
|
1185
1283
|
});
|
|
1284
|
+
const elapsed = Date.now() - t0;
|
|
1186
1285
|
const askId = args.askId;
|
|
1187
|
-
trace("permission.tool.approved", { tool: args.tool, askId });
|
|
1188
|
-
|
|
1189
|
-
hum({ chi: "release-permit", askId, decision: "allow" });
|
|
1190
|
-
}
|
|
1191
|
-
return `Permission granted for ${args.tool}`;
|
|
1286
|
+
trace("permission.tool.approved", { tool: args.tool, askId, elapsed, autoAllowed: elapsed < 100 });
|
|
1287
|
+
return JSON.stringify({ granted: true, tool: args.tool, askId });
|
|
1192
1288
|
}
|
|
1193
1289
|
})
|
|
1194
1290
|
}
|