@clwnd/opencode 0.13.1 → 0.14.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.
Files changed (2) hide show
  1. package/dist/index.js +130 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -152,6 +152,11 @@ var Drone = class _Drone {
152
152
  this.swallowThreshold = swallowThreshold;
153
153
  this.llmAssess = llmAssess;
154
154
  }
155
+ side;
156
+ onAction;
157
+ evaluate;
158
+ swallowThreshold;
159
+ llmAssess;
155
160
  states = /* @__PURE__ */ new Map();
156
161
  timers = /* @__PURE__ */ new Map();
157
162
  evaluating = /* @__PURE__ */ new Set();
@@ -661,8 +666,79 @@ function extractSystemPrompt(prompt) {
661
666
  }
662
667
  return parts.join("\n\n");
663
668
  }
669
+ function sanitizePrompt(text, word) {
670
+ if (!text) return text;
671
+ text = text.replace(/<\/?\w[\w-]*\b[^>]*>/g, "");
672
+ if (!word) {
673
+ return text.replace(/^\n+/, "");
674
+ }
675
+ const needle = new RegExp(word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
676
+ const lines = [];
677
+ {
678
+ let cursor = 0;
679
+ while (cursor < text.length) {
680
+ const nl = text.indexOf("\n", cursor);
681
+ if (nl === -1) {
682
+ lines.push(text.slice(cursor));
683
+ break;
684
+ }
685
+ lines.push(text.slice(cursor, nl + 1));
686
+ cursor = nl + 1;
687
+ }
688
+ }
689
+ const kindOf = (line) => {
690
+ const body = line.replace(/\n$/, "");
691
+ if (!body.trim()) return "blank";
692
+ if (/^\s*#/.test(body)) return "header";
693
+ if (/^\s*(?:[-*]|\d+\.)\s/.test(body)) return "list";
694
+ return "prose";
695
+ };
696
+ const isContinuation = (line) => {
697
+ const body = line.replace(/\n$/, "");
698
+ if (!body.trim()) return false;
699
+ return /^\s/.test(body) && !/^\s*(?:[-*]|\d+\.)\s/.test(body);
700
+ };
701
+ const units = [];
702
+ let i = 0;
703
+ while (i < lines.length) {
704
+ const k = kindOf(lines[i]);
705
+ if (k === "blank" || k === "header") {
706
+ units.push(lines[i]);
707
+ i++;
708
+ continue;
709
+ }
710
+ if (k === "list") {
711
+ let unit = lines[i];
712
+ i++;
713
+ while (i < lines.length && isContinuation(lines[i])) {
714
+ unit += lines[i];
715
+ i++;
716
+ }
717
+ units.push(unit);
718
+ continue;
719
+ }
720
+ let block = lines[i];
721
+ i++;
722
+ while (i < lines.length && kindOf(lines[i]) === "prose") {
723
+ block += lines[i];
724
+ i++;
725
+ }
726
+ const sentRe = /[.!?][ \t\n]+|\n/g;
727
+ let start = 0;
728
+ let m;
729
+ while ((m = sentRe.exec(block)) !== null) {
730
+ const end = m.index + m[0].length;
731
+ units.push(block.slice(start, end));
732
+ start = end;
733
+ }
734
+ if (start < block.length) units.push(block.slice(start));
735
+ }
736
+ const kept = units.filter((u) => !needle.test(u));
737
+ while (kept.length > 0 && !kept[0].trim()) kept.shift();
738
+ return kept.join("");
739
+ }
664
740
  function isAuxiliaryCall(opts) {
665
- return !opts.tools || !Array.isArray(opts.tools) || opts.tools.length === 0;
741
+ return opts.tools === void 0;
666
742
  }
667
743
  function isBrokeredToolReturn(prompt) {
668
744
  if (prompt.length < 2) return false;
@@ -779,11 +855,27 @@ var ClwndModel = class {
779
855
  this.config = config;
780
856
  this.modelId = modelId;
781
857
  }
858
+ config;
782
859
  specificationVersion = "v3";
783
860
  modelId;
784
861
  provider = "clwnd";
785
862
  supportedUrls = { "image/*": [] };
786
863
  async doGenerate(opts) {
864
+ const rawAgent = opts.headers?.["x-clwnd-agent"] ?? "";
865
+ let agentName = rawAgent;
866
+ try {
867
+ const p = JSON.parse(rawAgent);
868
+ if (p?.name) agentName = p.name;
869
+ } catch {
870
+ }
871
+ if (agentName === "title") {
872
+ return {
873
+ content: [{ type: "text", text: "" }],
874
+ usage: zeroUsage(),
875
+ finishReason: { unified: "stop", raw: "stop" },
876
+ warnings: []
877
+ };
878
+ }
787
879
  if (isAuxiliaryCall(opts)) {
788
880
  return {
789
881
  content: [{ type: "text", text: "" }],
@@ -818,15 +910,35 @@ var ClwndModel = class {
818
910
  trace("doStream.enter", { sid, promptLen: opts.prompt.length, lastRole });
819
911
  const content = extractContent(opts.prompt, sid);
820
912
  const text = content.filter((p) => p.type === "text").map((p) => p.text).join("\n\n");
821
- const systemPrompt = extractSystemPrompt(opts.prompt);
913
+ const systemPrompt = sanitizePrompt(extractSystemPrompt(opts.prompt), "opencode");
822
914
  detectAgent(sid, opts.headers);
823
915
  const cwd = (this.config.client ? await getSessionDirectory(this.config.client, sid) : null) ?? this.config.cwd ?? process.cwd();
824
916
  const self = this;
825
917
  const sap = /* @__PURE__ */ new Map();
826
918
  const permissions = await getSessionPermissions(this.config.client, sid);
827
919
  const allowedTools = deriveAllowedTools(sid, opts);
828
- const isAux = isAuxiliaryCall(opts);
829
- if (isAux) trace("auxiliary.passthrough", { method: "doStream", sid });
920
+ const rawAgent = opts.headers?.["x-clwnd-agent"] ?? "";
921
+ let agentName = rawAgent;
922
+ try {
923
+ const p = JSON.parse(rawAgent);
924
+ if (p?.name) agentName = p.name;
925
+ } catch {
926
+ }
927
+ const isTitleGen = agentName === "title";
928
+ const isEmptyTools = !opts.tools || opts.tools.length === 0;
929
+ if (isTitleGen) {
930
+ trace("title.skip", { method: "doStream", sid });
931
+ return {
932
+ stream: new ReadableStream({
933
+ start(controller) {
934
+ controller.enqueue({ type: "finish", finishReason: { unified: "stop", raw: "stop" }, usage: zeroUsage() });
935
+ controller.close();
936
+ }
937
+ })
938
+ };
939
+ }
940
+ const skipGraft = isEmptyTools;
941
+ if (skipGraft) trace("graft.skip", { method: "doStream", sid, reason: "emptyTools", toolsLen: opts.tools?.length ?? "undefined" });
830
942
  let permAskId = null;
831
943
  const isPermReturn = isBrokeredToolReturn(opts.prompt) && (() => {
832
944
  const lt = opts.prompt.findLast((m) => m.role === "tool");
@@ -860,8 +972,18 @@ var ClwndModel = class {
860
972
  }
861
973
  const listenOnly = !!isPermReturn;
862
974
  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) {
975
+ trace("priorPetals", {
976
+ sid,
977
+ count: priorPetals.length,
978
+ roles: priorPetals.map((m) => m.role).join(","),
979
+ hasToolUse: priorPetals.some(
980
+ (p) => p.role === "assistant" && Array.isArray(p.content) && p.content.some((c) => c.type === "tool-call")
981
+ ),
982
+ toolCallCount: priorPetals.filter(
983
+ (p) => p.role === "assistant" && Array.isArray(p.content)
984
+ ).reduce((acc, p) => acc + p.content.filter((c) => c.type === "tool-call").length, 0)
985
+ });
986
+ if (!skipGraft) {
865
987
  const prev = sessionPetalCounts.get(sid) ?? 0;
866
988
  sessionPetalCounts.set(sid, priorPetals.length);
867
989
  if (prev > 0 && priorPetals.length < prev * 0.5) {
@@ -910,8 +1032,7 @@ var ClwndModel = class {
910
1032
  permissions,
911
1033
  allowedTools,
912
1034
  listenOnly,
913
- auxiliary: isAux || void 0,
914
- // skip graft for compaction/title gen
1035
+ skipGraft: skipGraft || void 0,
915
1036
  ocServerUrl: self.config.pluginInput?.serverUrl?.toString(),
916
1037
  priorPetals,
917
1038
  externalTools: externalTools.length > 0 ? externalTools : void 0,
@@ -976,6 +1097,7 @@ var ClwndModel = class {
976
1097
  permissions,
977
1098
  allowedTools,
978
1099
  listenOnly,
1100
+ skipGraft: skipGraft || void 0,
979
1101
  ocServerUrl: self.config.pluginInput?.serverUrl?.toString(),
980
1102
  priorPetals,
981
1103
  dusk: duskIn(3e4)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clwnd/opencode",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "clwnd for opencode",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",