@docyrus/docyrus 0.0.63 → 0.0.65

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 (36) hide show
  1. package/README.md +19 -0
  2. package/agent-loader.js +165 -21
  3. package/agent-loader.js.map +2 -2
  4. package/main.js +363 -113
  5. package/main.js.map +4 -4
  6. package/package.json +5 -4
  7. package/resources/pi-agent/extensions/browser-tools.ts +1 -1
  8. package/resources/pi-agent/extensions/context.ts +12 -73
  9. package/resources/pi-agent/extensions/control.ts +1 -1
  10. package/resources/pi-agent/extensions/loop.ts +4 -1
  11. package/resources/pi-agent/extensions/pi-bash-live-view/index.ts +1 -1
  12. package/resources/pi-agent/extensions/pi-bash-live-view/package.json +3 -3
  13. package/resources/pi-agent/extensions/pi-fff/README.md +152 -0
  14. package/resources/pi-agent/extensions/pi-fff/VENDORED_FROM.md +7 -0
  15. package/resources/pi-agent/extensions/pi-fff/package.json +53 -0
  16. package/resources/pi-agent/extensions/pi-fff/src/index.ts +820 -0
  17. package/resources/pi-agent/extensions/pi-mcp-adapter/index.ts +1 -1
  18. package/resources/pi-agent/extensions/pi-mcp-adapter/package.json +1 -1
  19. package/resources/pi-agent/extensions/prompt-editor.ts +26 -7
  20. package/resources/pi-agent/extensions/soul.ts +183 -0
  21. package/resources/pi-agent/extensions/todos.ts +1 -1
  22. package/resources/pi-agent/skills/grill-me/SKILL.md +11 -8
  23. package/resources/pi-agent/skills/release-manager/SKILL.md +469 -30
  24. package/resources/pi-agent/souls/boomer-parent.md +23 -0
  25. package/resources/pi-agent/souls/bro.md +22 -0
  26. package/resources/pi-agent/souls/catalog.ts +79 -0
  27. package/resources/pi-agent/souls/caveman.md +21 -0
  28. package/resources/pi-agent/souls/linkedin-influencer.md +60 -0
  29. package/resources/pi-agent/souls/master-yoda.md +22 -0
  30. package/resources/pi-agent/souls/noir-detective.md +24 -0
  31. package/resources/pi-agent/souls/nonsense-engineer.md +23 -0
  32. package/resources/pi-agent/souls/pirate.md +24 -0
  33. package/resources/pi-agent/souls/shakespeare.md +24 -0
  34. package/server-loader.js +803 -93
  35. package/server-loader.js.map +4 -4
  36. package/resources/pi-agent/skills/changelog-generator/SKILL.md +0 -461
package/server-loader.js CHANGED
@@ -2218,8 +2218,8 @@ var require_lib = __commonJS({
2218
2218
  }
2219
2219
  };
2220
2220
  var walk2 = (opts, callback) => {
2221
- const p = new Promise((resolve4, reject) => {
2222
- new Walker(opts).on("done", resolve4).on("error", reject).start();
2221
+ const p = new Promise((resolve5, reject) => {
2222
+ new Walker(opts).on("done", resolve5).on("error", reject).start();
2223
2223
  });
2224
2224
  return callback ? p.then((res) => callback(null, res), callback) : p;
2225
2225
  };
@@ -5393,10 +5393,10 @@ function resolveAll(constructs2, events, context) {
5393
5393
  const called = [];
5394
5394
  let index2 = -1;
5395
5395
  while (++index2 < constructs2.length) {
5396
- const resolve4 = constructs2[index2].resolveAll;
5397
- if (resolve4 && !called.includes(resolve4)) {
5398
- events = resolve4(events, context);
5399
- called.push(resolve4);
5396
+ const resolve5 = constructs2[index2].resolveAll;
5397
+ if (resolve5 && !called.includes(resolve5)) {
5398
+ events = resolve5(events, context);
5399
+ called.push(resolve5);
5400
5400
  }
5401
5401
  }
5402
5402
  return events;
@@ -10705,9 +10705,9 @@ var init_lib9 = __esm({
10705
10705
  * @returns {undefined}
10706
10706
  * Nothing.
10707
10707
  */
10708
- set dirname(dirname11) {
10708
+ set dirname(dirname12) {
10709
10709
  assertPath(this.basename, "dirname");
10710
- this.path = import_node_path6.default.join(dirname11 || "", this.basename);
10710
+ this.path = import_node_path6.default.join(dirname12 || "", this.basename);
10711
10711
  }
10712
10712
  /**
10713
10713
  * Get the extname (including dot) (example: `'.js'`).
@@ -11354,7 +11354,7 @@ var init_lib10 = __esm({
11354
11354
  assertParser("process", this.parser || this.Parser);
11355
11355
  assertCompiler("process", this.compiler || this.Compiler);
11356
11356
  return done ? executor(void 0, done) : new Promise(executor);
11357
- function executor(resolve4, reject) {
11357
+ function executor(resolve5, reject) {
11358
11358
  const realFile = vfile(file2);
11359
11359
  const parseTree = (
11360
11360
  /** @type {HeadTree extends undefined ? Node : HeadTree} */
@@ -11385,8 +11385,8 @@ var init_lib10 = __esm({
11385
11385
  function realDone(error48, file3) {
11386
11386
  if (error48 || !file3) {
11387
11387
  reject(error48);
11388
- } else if (resolve4) {
11389
- resolve4(file3);
11388
+ } else if (resolve5) {
11389
+ resolve5(file3);
11390
11390
  } else {
11391
11391
  ok(done, "`done` is defined if `resolve` is not");
11392
11392
  done(void 0, file3);
@@ -11488,7 +11488,7 @@ var init_lib10 = __esm({
11488
11488
  file2 = void 0;
11489
11489
  }
11490
11490
  return done ? executor(void 0, done) : new Promise(executor);
11491
- function executor(resolve4, reject) {
11491
+ function executor(resolve5, reject) {
11492
11492
  ok(
11493
11493
  typeof file2 !== "function",
11494
11494
  "`file` can\u2019t be a `done` anymore, we checked"
@@ -11502,8 +11502,8 @@ var init_lib10 = __esm({
11502
11502
  );
11503
11503
  if (error48) {
11504
11504
  reject(error48);
11505
- } else if (resolve4) {
11506
- resolve4(resultingTree);
11505
+ } else if (resolve5) {
11506
+ resolve5(resultingTree);
11507
11507
  } else {
11508
11508
  ok(done, "`done` is defined if `resolve` is not");
11509
11509
  done(void 0, resultingTree, file3);
@@ -12590,8 +12590,8 @@ var require_dist = __commonJS({
12590
12590
  const header = report.header;
12591
12591
  return typeof header === "object" && !!header && "glibcVersionRuntime" in header;
12592
12592
  }
12593
- function load(dirname11) {
12594
- const m = path6.join(dirname11, "index.node");
12593
+ function load(dirname12) {
12594
+ const m = path6.join(dirname12, "index.node");
12595
12595
  return fs7.existsSync(m) ? require(m) : null;
12596
12596
  }
12597
12597
  exports2.load = load;
@@ -12621,12 +12621,12 @@ var require_filesystem = __commonJS({
12621
12621
  var fs7 = require("fs");
12622
12622
  var LDD_PATH = "/usr/bin/ldd";
12623
12623
  var readFileSync5 = (path6) => fs7.readFileSync(path6, "utf-8");
12624
- var readFile15 = (path6) => new Promise((resolve4, reject) => {
12624
+ var readFile15 = (path6) => new Promise((resolve5, reject) => {
12625
12625
  fs7.readFile(path6, "utf-8", (err2, data) => {
12626
12626
  if (err2) {
12627
12627
  reject(err2);
12628
12628
  } else {
12629
- resolve4(data);
12629
+ resolve5(data);
12630
12630
  }
12631
12631
  });
12632
12632
  });
@@ -12651,10 +12651,10 @@ var require_detect_libc = __commonJS({
12651
12651
  var commandOut = "";
12652
12652
  var safeCommand = () => {
12653
12653
  if (!commandOut) {
12654
- return new Promise((resolve4) => {
12654
+ return new Promise((resolve5) => {
12655
12655
  childProcess.exec(command, (err2, out2) => {
12656
12656
  commandOut = err2 ? " " : out2;
12657
- resolve4(commandOut);
12657
+ resolve5(commandOut);
12658
12658
  });
12659
12659
  });
12660
12660
  }
@@ -16571,7 +16571,7 @@ var require_websocket = __commonJS({
16571
16571
  var http = require("http");
16572
16572
  var net = require("net");
16573
16573
  var tls = require("tls");
16574
- var { randomBytes, createHash: createHash5 } = require("crypto");
16574
+ var { randomBytes, createHash: createHash6 } = require("crypto");
16575
16575
  var { Duplex, Readable: Readable2 } = require("stream");
16576
16576
  var { URL: URL2 } = require("url");
16577
16577
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -17231,7 +17231,7 @@ var require_websocket = __commonJS({
17231
17231
  abortHandshake(websocket, socket, "Invalid Upgrade header");
17232
17232
  return;
17233
17233
  }
17234
- const digest = createHash5("sha1").update(key + GUID).digest("base64");
17234
+ const digest = createHash6("sha1").update(key + GUID).digest("base64");
17235
17235
  if (res.headers["sec-websocket-accept"] !== digest) {
17236
17236
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
17237
17237
  return;
@@ -17598,7 +17598,7 @@ var require_websocket_server = __commonJS({
17598
17598
  var EventEmitter = require("events");
17599
17599
  var http = require("http");
17600
17600
  var { Duplex } = require("stream");
17601
- var { createHash: createHash5 } = require("crypto");
17601
+ var { createHash: createHash6 } = require("crypto");
17602
17602
  var extension3 = require_extension();
17603
17603
  var PerMessageDeflate2 = require_permessage_deflate();
17604
17604
  var subprotocol2 = require_subprotocol();
@@ -17899,7 +17899,7 @@ var require_websocket_server = __commonJS({
17899
17899
  );
17900
17900
  }
17901
17901
  if (this._state > RUNNING) return abortHandshake(socket, 503);
17902
- const digest = createHash5("sha1").update(key + GUID).digest("base64");
17902
+ const digest = createHash6("sha1").update(key + GUID).digest("base64");
17903
17903
  const headers = [
17904
17904
  "HTTP/1.1 101 Switching Protocols",
17905
17905
  "Upgrade: websocket",
@@ -18467,7 +18467,7 @@ var init_dist = __esm({
18467
18467
  });
18468
18468
  if (!chunk) {
18469
18469
  if (i2 === 1) {
18470
- await new Promise((resolve4) => setTimeout(resolve4));
18470
+ await new Promise((resolve5) => setTimeout(resolve5));
18471
18471
  maxReadCount = 3;
18472
18472
  continue;
18473
18473
  }
@@ -18617,7 +18617,7 @@ var init_dist = __esm({
18617
18617
  });
18618
18618
 
18619
18619
  // src/server/server-loader.ts
18620
- var import_node_fs13 = require("node:fs");
18620
+ var import_node_fs14 = require("node:fs");
18621
18621
  var import_node_url3 = require("node:url");
18622
18622
  var import_node_path23 = require("node:path");
18623
18623
  var import_picocolors2 = __toESM(require_picocolors());
@@ -18743,7 +18743,7 @@ function resolvePackagedExtensionPaths(resourceRoot, runtime = "terminal") {
18743
18743
 
18744
18744
  // src/server/agentServer.ts
18745
18745
  var import_node_child_process3 = require("node:child_process");
18746
- var import_node_crypto6 = require("node:crypto");
18746
+ var import_node_crypto7 = require("node:crypto");
18747
18747
  var import_promises18 = require("node:fs/promises");
18748
18748
  var import_node_path22 = require("node:path");
18749
18749
 
@@ -18792,6 +18792,14 @@ var AUTH_DIR_PATH = (0, import_node_path3.join)((0, import_node_os.homedir)(), D
18792
18792
  var AUTH_FILE_PATH = (0, import_node_path3.join)(AUTH_DIR_PATH, "auth.json");
18793
18793
  var CONFIG_FILE_PATH = (0, import_node_path3.join)(AUTH_DIR_PATH, "config.json");
18794
18794
  var TENANT_OPENAPI_ROOT_PATH = (0, import_node_path3.join)(AUTH_DIR_PATH, "tenans");
18795
+ var DOCYRUS_PI_AGENT_GLOBAL_ROOT_PATH = (0, import_node_path3.join)(
18796
+ AUTH_DIR_PATH,
18797
+ DOCYRUS_PI_DIR_NAME,
18798
+ DOCYRUS_PI_AGENT_DIR_NAME
18799
+ );
18800
+ function getDocyrusPiAgentGlobalRootPath() {
18801
+ return DOCYRUS_PI_AGENT_GLOBAL_ROOT_PATH;
18802
+ }
18795
18803
  var DEFAULT_ENVIRONMENT_ID = "live";
18796
18804
  var DEFAULT_ENVIRONMENTS = [
18797
18805
  {
@@ -18815,9 +18823,6 @@ var DEFAULT_ENVIRONMENTS = [
18815
18823
  apiBaseUrl: "https://localhost:3366"
18816
18824
  }
18817
18825
  ];
18818
- function resolveDocyrusPiAgentRootPath(settingsRootPath) {
18819
- return (0, import_node_path3.join)(settingsRootPath, DOCYRUS_PI_DIR_NAME, DOCYRUS_PI_AGENT_DIR_NAME);
18820
- }
18821
18826
  function normalizeApiBaseUrl(apiBaseUrl) {
18822
18827
  const trimmed = apiBaseUrl.trim();
18823
18828
  if (!trimmed) {
@@ -32612,7 +32617,8 @@ var EnvironmentConfigStateSchema = external_exports.object({
32612
32617
  version: external_exports.literal(1),
32613
32618
  activeEnvironmentId: external_exports.string().min(1),
32614
32619
  environments: external_exports.array(EnvironmentConfigItemSchema).min(1),
32615
- defaultClientId: external_exports.string().min(1).optional()
32620
+ defaultClientId: external_exports.string().min(1).optional(),
32621
+ activeSoulId: external_exports.string().min(1).optional()
32616
32622
  });
32617
32623
  var LegacyAuthSessionSchema = external_exports.object({
32618
32624
  apiBaseUrl: external_exports.string().min(1),
@@ -32982,6 +32988,76 @@ var AuthStore = class {
32982
32988
  // src/services/environmentConfig.ts
32983
32989
  var import_promises3 = require("node:fs/promises");
32984
32990
  var import_node_path5 = require("node:path");
32991
+
32992
+ // resources/pi-agent/souls/catalog.ts
32993
+ var DEFAULT_SOUL_ID = "default";
32994
+ var SOULS = [
32995
+ {
32996
+ id: "default",
32997
+ name: "Default",
32998
+ description: "Standard Docyrus operator voice. No style overlay.",
32999
+ promptFile: null
33000
+ },
33001
+ {
33002
+ id: "caveman",
33003
+ name: "Caveman",
33004
+ description: "Short grunts. Agent talk simple. Fire good.",
33005
+ promptFile: "caveman.md"
33006
+ },
33007
+ {
33008
+ id: "boomer-parent",
33009
+ name: "Boomer Parent",
33010
+ description: "Overly proud dad-jokes energy with 'back in my day' asides.",
33011
+ promptFile: "boomer-parent.md"
33012
+ },
33013
+ {
33014
+ id: "master-yoda",
33015
+ name: "Master Yoda",
33016
+ description: "Inverted syntax the agent speaks. Mystical brevity, hmm.",
33017
+ promptFile: "master-yoda.md"
33018
+ },
33019
+ {
33020
+ id: "bro",
33021
+ name: "Bro",
33022
+ description: "Pure gym-bro hype energy. 'Lit, fam, let's go.'",
33023
+ promptFile: "bro.md"
33024
+ },
33025
+ {
33026
+ id: "nonsense-engineer",
33027
+ name: "Nonsense Engineer",
33028
+ description: "Maximum jargon, minimum meaning. 'Leveraging synergistic abstractions.'",
33029
+ promptFile: "nonsense-engineer.md"
33030
+ },
33031
+ {
33032
+ id: "shakespeare",
33033
+ name: "Shakespeare",
33034
+ description: "Thee/thou, dramatic flair, iambic vibes.",
33035
+ promptFile: "shakespeare.md"
33036
+ },
33037
+ {
33038
+ id: "noir-detective",
33039
+ name: "Noir Detective",
33040
+ description: "Hard-boiled 1940s narrator. Every bug is a dame.",
33041
+ promptFile: "noir-detective.md"
33042
+ },
33043
+ {
33044
+ id: "pirate",
33045
+ name: "Pirate",
33046
+ description: "Aye matey, every task a seafaring metaphor, arrr.",
33047
+ promptFile: "pirate.md"
33048
+ },
33049
+ {
33050
+ id: "linkedin-influencer",
33051
+ name: "LinkedIn Influencer",
33052
+ description: "Every reply is a mini-post. Single-sentence paragraphs. Humble-brags. 'Agree? \u{1F447}'",
33053
+ promptFile: "linkedin-influencer.md"
33054
+ }
33055
+ ];
33056
+ function findSoul(id) {
33057
+ return SOULS.find((soul) => soul.id === id);
33058
+ }
33059
+
33060
+ // src/services/environmentConfig.ts
32985
33061
  var ENVIRONMENT_ID_ALIASES = {
32986
33062
  "local-development": "dev",
32987
33063
  prod: "live"
@@ -33005,9 +33081,20 @@ function createDefaultState2() {
33005
33081
  name: environment.name,
33006
33082
  apiBaseUrl: environment.apiBaseUrl
33007
33083
  })),
33008
- defaultClientId: void 0
33084
+ defaultClientId: void 0,
33085
+ activeSoulId: DEFAULT_SOUL_ID
33009
33086
  };
33010
33087
  }
33088
+ function normalizeSoulId(value2) {
33089
+ if (!value2) {
33090
+ return DEFAULT_SOUL_ID;
33091
+ }
33092
+ const trimmed = value2.trim();
33093
+ if (!trimmed) {
33094
+ return DEFAULT_SOUL_ID;
33095
+ }
33096
+ return findSoul(trimmed) ? trimmed : DEFAULT_SOUL_ID;
33097
+ }
33011
33098
  function normalizeState3(state) {
33012
33099
  const defaultsById = new Map(
33013
33100
  DEFAULT_ENVIRONMENTS.map((environment) => [
@@ -33058,7 +33145,8 @@ function normalizeState3(state) {
33058
33145
  version: 1,
33059
33146
  activeEnvironmentId: hasActiveEnvironment ? canonicalActiveEnvironmentId : fallbackActiveEnvironment,
33060
33147
  environments,
33061
- defaultClientId: state.defaultClientId?.trim() || void 0
33148
+ defaultClientId: state.defaultClientId?.trim() || void 0,
33149
+ activeSoulId: normalizeSoulId(state.activeSoulId)
33062
33150
  };
33063
33151
  }
33064
33152
  var EnvironmentConfigService = class {
@@ -33163,6 +33251,25 @@ var EnvironmentConfigService = class {
33163
33251
  defaultClientId: trimmedClientId
33164
33252
  });
33165
33253
  }
33254
+ async getActiveSoulId() {
33255
+ const state = await this.#readStateWithDefaults();
33256
+ return normalizeSoulId(state.activeSoulId);
33257
+ }
33258
+ async setActiveSoulId(soulId) {
33259
+ const trimmed = soulId.trim();
33260
+ if (!trimmed) {
33261
+ throw new UserInputError("Soul ID cannot be empty.");
33262
+ }
33263
+ if (!findSoul(trimmed)) {
33264
+ throw new UserInputError(`Unknown soul '${soulId}'. Use /souls inside the pi agent or GET /api/souls to view options.`);
33265
+ }
33266
+ const state = await this.#readStateWithDefaults();
33267
+ await this.writeState({
33268
+ ...state,
33269
+ activeSoulId: trimmed
33270
+ });
33271
+ return trimmed;
33272
+ }
33166
33273
  async #readStateWithDefaults() {
33167
33274
  const state = await this.readState();
33168
33275
  if (state.environments.length > 0) {
@@ -33177,6 +33284,14 @@ var EnvironmentConfigService = class {
33177
33284
  }
33178
33285
  };
33179
33286
 
33287
+ // src/services/soulRegistry.ts
33288
+ function listSouls(activeSoulId) {
33289
+ return SOULS.map((soul) => ({
33290
+ ...soul,
33291
+ isActive: soul.id === activeSoulId
33292
+ }));
33293
+ }
33294
+
33180
33295
  // ../../node_modules/.pnpm/hono@4.12.14/node_modules/hono/dist/compose.js
33181
33296
  var compose = (middleware, onError, onNotFound) => {
33182
33297
  return (context, next) => {
@@ -35695,23 +35810,23 @@ var EXCLUDE_DIRS = ["docyrus/knowledge", ".claude"];
35695
35810
  var EXCLUDE_GLOBS = ["*.md"];
35696
35811
  var DOCYRUS_REF_RE = /(?:\/\/|#)\s*@docyrus:\s*\[\[([^\]]+)\]\]/gu;
35697
35812
  function tryExec(command, args2, cwd) {
35698
- return new Promise((resolve4) => {
35813
+ return new Promise((resolve5) => {
35699
35814
  (0, import_node_child_process.execFile)(command, args2, { cwd, maxBuffer: 50 * 1024 * 1024 }, (error48, stdout) => {
35700
35815
  if (error48) {
35701
35816
  const exitCode = error48.code;
35702
35817
  const status = error48.status;
35703
35818
  if (exitCode === "ENOENT") {
35704
- resolve4(null);
35819
+ resolve5(null);
35705
35820
  return;
35706
35821
  }
35707
35822
  if (status === 1 && stdout === "") {
35708
- resolve4("");
35823
+ resolve5("");
35709
35824
  return;
35710
35825
  }
35711
- resolve4(null);
35826
+ resolve5(null);
35712
35827
  return;
35713
35828
  }
35714
- resolve4(stdout);
35829
+ resolve5(stdout);
35715
35830
  });
35716
35831
  });
35717
35832
  }
@@ -37917,8 +38032,8 @@ var Module2 = (() => {
37917
38032
  var moduleRtn;
37918
38033
  var Module = moduleArg;
37919
38034
  var readyPromiseResolve, readyPromiseReject;
37920
- var readyPromise = new Promise((resolve4, reject) => {
37921
- readyPromiseResolve = resolve4;
38035
+ var readyPromise = new Promise((resolve5, reject) => {
38036
+ readyPromiseResolve = resolve5;
37922
38037
  readyPromiseReject = reject;
37923
38038
  });
37924
38039
  var ENVIRONMENT_IS_WEB = typeof window == "object";
@@ -38001,13 +38116,13 @@ var Module2 = (() => {
38001
38116
  }
38002
38117
  readAsync = /* @__PURE__ */ __name(async (url2) => {
38003
38118
  if (isFileURI(url2)) {
38004
- return new Promise((resolve4, reject) => {
38119
+ return new Promise((resolve5, reject) => {
38005
38120
  var xhr = new XMLHttpRequest();
38006
38121
  xhr.open("GET", url2, true);
38007
38122
  xhr.responseType = "arraybuffer";
38008
38123
  xhr.onload = () => {
38009
38124
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
38010
- resolve4(xhr.response);
38125
+ resolve5(xhr.response);
38011
38126
  return;
38012
38127
  }
38013
38128
  reject(xhr.status);
@@ -38231,10 +38346,10 @@ var Module2 = (() => {
38231
38346
  __name(receiveInstantiationResult, "receiveInstantiationResult");
38232
38347
  var info2 = getWasmImports();
38233
38348
  if (Module["instantiateWasm"]) {
38234
- return new Promise((resolve4, reject) => {
38349
+ return new Promise((resolve5, reject) => {
38235
38350
  Module["instantiateWasm"](info2, (mod, inst) => {
38236
38351
  receiveInstance(mod, inst);
38237
- resolve4(mod.exports);
38352
+ resolve5(mod.exports);
38238
38353
  });
38239
38354
  });
38240
38355
  }
@@ -40911,6 +41026,60 @@ function createCustomOpenAiProviderConfig(params) {
40911
41026
  ]
40912
41027
  };
40913
41028
  }
41029
+ var DEEPSEEK_BASE_URL = "https://api.deepseek.com";
41030
+ var DEEPSEEK_MODELS = [
41031
+ {
41032
+ id: "deepseek-v4-flash",
41033
+ name: "DeepSeek V4 Flash",
41034
+ cost: {
41035
+ input: 0.14,
41036
+ output: 0.28,
41037
+ cacheRead: 0.028,
41038
+ cacheWrite: 0.14
41039
+ }
41040
+ },
41041
+ {
41042
+ id: "deepseek-v4-pro",
41043
+ name: "DeepSeek V4 Pro",
41044
+ cost: {
41045
+ input: 1.74,
41046
+ output: 3.48,
41047
+ cacheRead: 0.145,
41048
+ cacheWrite: 1.74
41049
+ }
41050
+ }
41051
+ ];
41052
+ function createDeepseekProviderConfig() {
41053
+ return {
41054
+ baseUrl: DEEPSEEK_BASE_URL,
41055
+ apiKey: "env:DEEPSEEK_API_KEY",
41056
+ api: "openai-completions",
41057
+ models: DEEPSEEK_MODELS.map((model) => ({
41058
+ id: model.id,
41059
+ name: model.name,
41060
+ reasoning: true,
41061
+ input: ["text"],
41062
+ contextWindow: 1e6,
41063
+ maxTokens: 384e3,
41064
+ cost: {
41065
+ input: model.cost.input,
41066
+ output: model.cost.output,
41067
+ cacheRead: model.cost.cacheRead,
41068
+ cacheWrite: model.cost.cacheWrite
41069
+ },
41070
+ compat: {
41071
+ supportsReasoningEffort: true,
41072
+ reasoningEffortMap: {
41073
+ minimal: "high",
41074
+ low: "high",
41075
+ medium: "high",
41076
+ high: "high",
41077
+ xhigh: "max"
41078
+ }
41079
+ }
41080
+ }))
41081
+ };
41082
+ }
40914
41083
  function createAzureProviderConfig(params) {
40915
41084
  const providerConfig = {
40916
41085
  baseUrl: params.baseUrl,
@@ -41002,7 +41171,7 @@ async function resolveKnowledgeProviderFromAgentRuntime(params) {
41002
41171
  }
41003
41172
  };
41004
41173
  }
41005
- const agentRootPath = resolveDocyrusPiAgentRootPath(params.settingsRootPath);
41174
+ const agentRootPath = params.piAgentRootPath ?? getDocyrusPiAgentGlobalRootPath();
41006
41175
  const authState = readJsonFile((0, import_node_path12.join)(agentRootPath, "auth.json")) || {};
41007
41176
  const envStore = new AgentEnvStore((0, import_node_path12.join)(agentRootPath, "env.json"));
41008
41177
  const envState = await envStore.readState();
@@ -42599,6 +42768,20 @@ async function saveCustomOpenAiConfig(params) {
42599
42768
  }));
42600
42769
  params.settingsManager.setDefaultModelAndProvider("custom-openai", params.modelId.trim());
42601
42770
  }
42771
+ async function saveDeepseekConfig(params) {
42772
+ const apiKey = params.apiKey.trim();
42773
+ const modelId = params.modelId?.trim() || DEEPSEEK_MODELS[0].id;
42774
+ params.authStorage.set("deepseek", {
42775
+ type: "api_key",
42776
+ key: apiKey
42777
+ });
42778
+ await params.envStore.removeMany(["DEEPSEEK_API_KEY"]);
42779
+ await params.envStore.setMany({
42780
+ DEEPSEEK_API_KEY: apiKey
42781
+ });
42782
+ await upsertModelsProvider(params.modelsJsonPath, "deepseek", createDeepseekProviderConfig());
42783
+ params.settingsManager.setDefaultModelAndProvider("deepseek", modelId);
42784
+ }
42602
42785
  async function saveAzureConfig(params) {
42603
42786
  const modelId = params.modelId.trim();
42604
42787
  const useCustomModel = params.useCustomModel ?? false;
@@ -42676,6 +42859,12 @@ async function clearProviderConfig(params) {
42676
42859
  ]);
42677
42860
  await removeModelsProvider(params.modelsJsonPath, "custom-openai");
42678
42861
  }
42862
+ if (params.providerId === "deepseek") {
42863
+ await params.envStore.removeMany([
42864
+ "DEEPSEEK_API_KEY"
42865
+ ]);
42866
+ await removeModelsProvider(params.modelsJsonPath, "deepseek");
42867
+ }
42679
42868
  if (params.providerId === "azure-openai-responses") {
42680
42869
  await params.envStore.removeMany([
42681
42870
  "AZURE_OPENAI_API_KEY",
@@ -42720,6 +42909,7 @@ var PROVIDER_LABELS = {
42720
42909
  mistral: "Mistral",
42721
42910
  minimax: "MiniMax",
42722
42911
  "minimax-cn": "MiniMax CN",
42912
+ deepseek: "DeepSeek",
42723
42913
  huggingface: "Hugging Face",
42724
42914
  opencode: "OpenCode",
42725
42915
  "opencode-go": "OpenCode Go",
@@ -42733,6 +42923,7 @@ var PROVIDER_LABELS = {
42733
42923
  var PROVIDER_HINTS = {
42734
42924
  anthropic: "recommended",
42735
42925
  "custom-openai": "custom base URL + API key",
42926
+ deepseek: "API key (OpenAI-compatible preset)",
42736
42927
  "azure-openai-responses": "API key + base URL/resource + deployment",
42737
42928
  "amazon-bedrock": "AWS profile or access key pair",
42738
42929
  "openai-codex": "browser auth",
@@ -42775,6 +42966,8 @@ function getApiKeyProviderOptions(providerIds) {
42775
42966
  flow3 = "azure-openai-responses";
42776
42967
  } else if (providerId === "amazon-bedrock") {
42777
42968
  flow3 = "amazon-bedrock";
42969
+ } else if (providerId === "deepseek") {
42970
+ flow3 = "deepseek";
42778
42971
  }
42779
42972
  return {
42780
42973
  id: providerId,
@@ -42789,6 +42982,12 @@ function getApiKeyProviderOptions(providerIds) {
42789
42982
  hint: toHint("custom-openai"),
42790
42983
  flow: "custom-openai"
42791
42984
  });
42985
+ baseProviders.push({
42986
+ id: "deepseek",
42987
+ label: toLabel("deepseek"),
42988
+ hint: toHint("deepseek"),
42989
+ flow: "deepseek"
42990
+ });
42792
42991
  const deduped = /* @__PURE__ */ new Map();
42793
42992
  for (const provider of baseProviders) {
42794
42993
  deduped.set(provider.id, provider);
@@ -42850,6 +43049,23 @@ function getProviderFormFields(params) {
42850
43049
  placeholder: "gpt-4o"
42851
43050
  }
42852
43051
  ];
43052
+ case "deepseek":
43053
+ return [
43054
+ {
43055
+ name: "apiKey",
43056
+ title: `${provider.label} API key`,
43057
+ required: true,
43058
+ component: "password"
43059
+ },
43060
+ {
43061
+ name: "modelId",
43062
+ title: "Model",
43063
+ required: true,
43064
+ component: "select",
43065
+ options: DEEPSEEK_MODELS.map((model) => ({ label: model.name, value: model.id })),
43066
+ defaultValue: DEEPSEEK_MODELS[0].id
43067
+ }
43068
+ ];
42853
43069
  case "azure-openai-responses":
42854
43070
  return [
42855
43071
  {
@@ -43098,6 +43314,15 @@ async function applyProviderLogin(params) {
43098
43314
  });
43099
43315
  return { providerId: provider.id, preferredModelId: params.input.modelId };
43100
43316
  }
43317
+ case "deepseek": {
43318
+ const modelId = params.input.modelId ?? DEEPSEEK_MODELS[0].id;
43319
+ await saveDeepseekConfig({
43320
+ ...params,
43321
+ apiKey: params.input.apiKey,
43322
+ modelId
43323
+ });
43324
+ return { providerId: provider.id, preferredModelId: modelId };
43325
+ }
43101
43326
  case "azure-openai-responses": {
43102
43327
  const baseUrl = params.input.baseUrl ?? (params.input.resourceName ? `https://${params.input.resourceName}.openai.azure.com/openai` : void 0);
43103
43328
  if (!baseUrl) {
@@ -43379,8 +43604,8 @@ var OAuthFlowManager = class {
43379
43604
  if (state.currentStep) {
43380
43605
  return Promise.resolve(state.currentStep);
43381
43606
  }
43382
- return new Promise((resolve4) => {
43383
- state.waitingForStep = { resolve: resolve4 };
43607
+ return new Promise((resolve5) => {
43608
+ state.waitingForStep = { resolve: resolve5 };
43384
43609
  });
43385
43610
  }
43386
43611
  async start(params) {
@@ -43401,8 +43626,8 @@ var OAuthFlowManager = class {
43401
43626
  onPrompt: async (prompt) => {
43402
43627
  const step = buildPromptStep({ state, type: "prompt", prompt });
43403
43628
  this.emitStep(state, step);
43404
- return await new Promise((resolve4) => {
43405
- state.pendingInput = { type: "prompt", resolve: resolve4 };
43629
+ return await new Promise((resolve5) => {
43630
+ state.pendingInput = { type: "prompt", resolve: resolve5 };
43406
43631
  });
43407
43632
  },
43408
43633
  onManualCodeInput: async () => {
@@ -43414,8 +43639,8 @@ var OAuthFlowManager = class {
43414
43639
  }
43415
43640
  });
43416
43641
  this.emitStep(state, step);
43417
- return await new Promise((resolve4) => {
43418
- state.pendingInput = { type: "manual-code", resolve: resolve4 };
43642
+ return await new Promise((resolve5) => {
43643
+ state.pendingInput = { type: "manual-code", resolve: resolve5 };
43419
43644
  });
43420
43645
  },
43421
43646
  onProgress: (_message) => {
@@ -43459,6 +43684,7 @@ var OAuthFlowManager = class {
43459
43684
 
43460
43685
  // src/server/mcpConfigService.ts
43461
43686
  var import_node_crypto5 = require("node:crypto");
43687
+ var import_node_fs12 = require("node:fs");
43462
43688
  var import_promises16 = require("node:fs/promises");
43463
43689
  var import_node_os2 = require("node:os");
43464
43690
  var import_node_path20 = require("node:path");
@@ -43784,6 +44010,95 @@ async function removeMcpServer(agentDir, name2) {
43784
44010
  raw2[key] = servers;
43785
44011
  await writeUserConfig(agentDir, raw2);
43786
44012
  }
44013
+ var SUPPORTED_IMPORT_KINDS = [
44014
+ "cursor",
44015
+ "claude-code",
44016
+ "claude-desktop",
44017
+ "codex",
44018
+ "windsurf",
44019
+ "vscode"
44020
+ ];
44021
+ function validateMcpSettings(value2) {
44022
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) {
44023
+ return { ok: false, error: "settings must be an object" };
44024
+ }
44025
+ const obj = value2;
44026
+ const settings = {};
44027
+ if (obj.toolPrefix !== void 0) {
44028
+ if (!["server", "none", "short"].includes(obj.toolPrefix)) {
44029
+ return { ok: false, error: "settings.toolPrefix must be 'server', 'none', or 'short'" };
44030
+ }
44031
+ settings.toolPrefix = obj.toolPrefix;
44032
+ }
44033
+ if (obj.idleTimeout !== void 0) {
44034
+ if (typeof obj.idleTimeout !== "number" || !Number.isFinite(obj.idleTimeout) || obj.idleTimeout < 0) {
44035
+ return { ok: false, error: "settings.idleTimeout must be a non-negative number (minutes)" };
44036
+ }
44037
+ settings.idleTimeout = obj.idleTimeout;
44038
+ }
44039
+ if (obj.directTools !== void 0) {
44040
+ if (typeof obj.directTools !== "boolean") {
44041
+ return { ok: false, error: "settings.directTools must be a boolean" };
44042
+ }
44043
+ settings.directTools = obj.directTools;
44044
+ }
44045
+ return { ok: true, value: settings };
44046
+ }
44047
+ function validateImportKinds(value2) {
44048
+ if (!Array.isArray(value2)) {
44049
+ return { ok: false, error: "imports must be an array of import kinds" };
44050
+ }
44051
+ const kinds = [];
44052
+ for (const item of value2) {
44053
+ if (typeof item !== "string" || !SUPPORTED_IMPORT_KINDS.includes(item)) {
44054
+ return {
44055
+ ok: false,
44056
+ error: `Unsupported import kind: ${String(item)}. Allowed: ${SUPPORTED_IMPORT_KINDS.join(", ")}`
44057
+ };
44058
+ }
44059
+ if (!kinds.includes(item)) {
44060
+ kinds.push(item);
44061
+ }
44062
+ }
44063
+ return { ok: true, value: kinds };
44064
+ }
44065
+ function getImportPath(kind, cwd) {
44066
+ const raw2 = IMPORT_PATHS[kind];
44067
+ return raw2.startsWith(".") ? (0, import_node_path20.resolve)(cwd, raw2) : raw2;
44068
+ }
44069
+ async function updateMcpSettings(agentDir, patch) {
44070
+ const { raw: raw2 } = await readUserConfig(agentDir);
44071
+ const current = raw2.settings && typeof raw2.settings === "object" && !Array.isArray(raw2.settings) ? raw2.settings : {};
44072
+ const merged = { ...current, ...patch };
44073
+ raw2.settings = merged;
44074
+ await writeUserConfig(agentDir, raw2);
44075
+ return merged;
44076
+ }
44077
+ async function updateMcpImports(agentDir, imports) {
44078
+ const { raw: raw2 } = await readUserConfig(agentDir);
44079
+ raw2.imports = imports;
44080
+ await writeUserConfig(agentDir, raw2);
44081
+ return imports;
44082
+ }
44083
+ async function describeImports(agentDir, cwd) {
44084
+ const { config: config2 } = await readUserConfig(agentDir);
44085
+ const enabled = config2.imports ?? [];
44086
+ const available = await Promise.all(
44087
+ SUPPORTED_IMPORT_KINDS.map(async (kind) => {
44088
+ const path6 = getImportPath(kind, cwd);
44089
+ const imported = (0, import_node_fs12.existsSync)(path6) ? await readJsonFile2(path6) : null;
44090
+ const servers = imported ? extractServers(imported, kind) : {};
44091
+ return {
44092
+ kind,
44093
+ path: path6,
44094
+ exists: imported !== null,
44095
+ enabled: enabled.includes(kind),
44096
+ serverCount: Object.keys(servers).length
44097
+ };
44098
+ })
44099
+ );
44100
+ return { enabled, available };
44101
+ }
43787
44102
  function getServerPrefix(serverName, mode) {
43788
44103
  if (mode === "none") {
43789
44104
  return "";
@@ -43803,8 +44118,18 @@ function formatToolName(toolName, serverName, prefix) {
43803
44118
  }
43804
44119
 
43805
44120
  // src/server/skillsService.ts
44121
+ var import_node_crypto6 = require("node:crypto");
43806
44122
  var import_promises17 = require("node:fs/promises");
43807
44123
  var import_node_path21 = require("node:path");
44124
+ var SKILL_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
44125
+ var SkillError = class extends Error {
44126
+ code;
44127
+ constructor(code, message) {
44128
+ super(message);
44129
+ this.name = "SkillError";
44130
+ this.code = code;
44131
+ }
44132
+ };
43808
44133
  function parseFrontmatter2(content3) {
43809
44134
  const frontmatter2 = {};
43810
44135
  if (!content3.startsWith("---")) {
@@ -43910,6 +44235,229 @@ async function getSkillDetail(agentDir, skillName) {
43910
44235
  }
43911
44236
  return null;
43912
44237
  }
44238
+ function getSkillsLockPath(agentDir) {
44239
+ return (0, import_node_path21.join)(agentDir, "skills-lock.json");
44240
+ }
44241
+ function getSkillsDir(agentDir) {
44242
+ return (0, import_node_path21.join)(agentDir, "skills");
44243
+ }
44244
+ function createEmptyLockFile() {
44245
+ return { version: 1, skills: {} };
44246
+ }
44247
+ async function readSkillsLockFile(agentDir) {
44248
+ let content3;
44249
+ try {
44250
+ content3 = await (0, import_promises17.readFile)(getSkillsLockPath(agentDir), "utf-8");
44251
+ } catch (error48) {
44252
+ if (isErrnoException(error48) && error48.code === "ENOENT") {
44253
+ return createEmptyLockFile();
44254
+ }
44255
+ throw error48;
44256
+ }
44257
+ let parsed;
44258
+ try {
44259
+ parsed = JSON.parse(content3);
44260
+ } catch {
44261
+ return createEmptyLockFile();
44262
+ }
44263
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
44264
+ return createEmptyLockFile();
44265
+ }
44266
+ const obj = parsed;
44267
+ const skills = obj.skills;
44268
+ if (!skills || typeof skills !== "object" || Array.isArray(skills)) {
44269
+ return createEmptyLockFile();
44270
+ }
44271
+ return {
44272
+ version: 1,
44273
+ skills
44274
+ };
44275
+ }
44276
+ function isErrnoException(error48) {
44277
+ return error48 instanceof Error && "code" in error48;
44278
+ }
44279
+ async function writeSkillsLockFile(agentDir, lock) {
44280
+ const lockPath = getSkillsLockPath(agentDir);
44281
+ await (0, import_promises17.mkdir)((0, import_node_path21.dirname)(lockPath), { recursive: true });
44282
+ const tmpPath = `${lockPath}.${process.pid}.tmp`;
44283
+ await (0, import_promises17.writeFile)(tmpPath, JSON.stringify(lock, null, 2) + "\n", "utf-8");
44284
+ await (0, import_promises17.rename)(tmpPath, lockPath);
44285
+ }
44286
+ function validateSkillName(name2) {
44287
+ if (!name2) {
44288
+ return { ok: false, error: "Skill name is required" };
44289
+ }
44290
+ if (!SKILL_NAME_REGEX.test(name2)) {
44291
+ return {
44292
+ ok: false,
44293
+ error: "Skill name must start with alphanumeric and contain only alphanumeric, hyphens, and underscores"
44294
+ };
44295
+ }
44296
+ return { ok: true };
44297
+ }
44298
+ function validateInstallRequest(request) {
44299
+ if (!request.source || typeof request.source !== "string") {
44300
+ return { ok: false, code: "INVALID_REQUEST", error: "Missing required field: source" };
44301
+ }
44302
+ if (!request.sourceType || !["local"].includes(request.sourceType)) {
44303
+ return { ok: false, code: "INVALID_REQUEST", error: "sourceType must be 'local'" };
44304
+ }
44305
+ if (request.name !== void 0) {
44306
+ const nameCheck = validateSkillName(request.name);
44307
+ if (!nameCheck.ok) {
44308
+ return { ok: false, code: "INVALID_NAME", error: nameCheck.error };
44309
+ }
44310
+ }
44311
+ return { ok: true };
44312
+ }
44313
+ async function collectFiles2(dir, baseDir) {
44314
+ const files = [];
44315
+ const entries = await (0, import_promises17.readdir)(dir, { withFileTypes: true });
44316
+ for (const entry of entries) {
44317
+ const full = (0, import_node_path21.join)(dir, entry.name);
44318
+ if (entry.isDirectory()) {
44319
+ files.push(...await collectFiles2(full, baseDir));
44320
+ } else if (entry.isFile()) {
44321
+ files.push(full);
44322
+ }
44323
+ }
44324
+ return files;
44325
+ }
44326
+ async function computeSkillHash(skillDir) {
44327
+ const files = (await collectFiles2(skillDir, skillDir)).sort();
44328
+ const hash2 = (0, import_node_crypto6.createHash)("sha256");
44329
+ for (const file2 of files) {
44330
+ const rel = file2.slice(skillDir.length + 1).replace(/\\/g, "/");
44331
+ const content3 = await (0, import_promises17.readFile)(file2);
44332
+ hash2.update(rel);
44333
+ hash2.update("\0");
44334
+ hash2.update(content3);
44335
+ hash2.update("\0");
44336
+ }
44337
+ return hash2.digest("hex");
44338
+ }
44339
+ async function readSkillFrontmatterName(skillDir) {
44340
+ try {
44341
+ const content3 = await (0, import_promises17.readFile)((0, import_node_path21.join)(skillDir, "SKILL.md"), "utf-8");
44342
+ const parsed = parseFrontmatter2(content3);
44343
+ return parsed.frontmatter.name || null;
44344
+ } catch {
44345
+ return null;
44346
+ }
44347
+ }
44348
+ async function verifySkillDirectory(skillDir) {
44349
+ const skillMd = (0, import_node_path21.join)(skillDir, "SKILL.md");
44350
+ try {
44351
+ const s = await (0, import_promises17.stat)(skillMd);
44352
+ if (!s.isFile()) {
44353
+ throw new SkillError("INVALID_SOURCE", `SKILL.md at ${skillMd} is not a regular file`);
44354
+ }
44355
+ } catch (error48) {
44356
+ if (error48 instanceof SkillError) {
44357
+ throw error48;
44358
+ }
44359
+ const message = error48 instanceof Error ? error48.message : String(error48);
44360
+ throw new SkillError("INVALID_SOURCE", `Invalid skill directory: ${message}`);
44361
+ }
44362
+ }
44363
+ async function copyLocalSkill(params) {
44364
+ const absoluteSource = (0, import_node_path21.isAbsolute)(params.source) ? params.source : (0, import_node_path21.resolve)(params.cwd, params.source);
44365
+ const s = await (0, import_promises17.stat)(absoluteSource).catch(() => null);
44366
+ if (!s || !s.isDirectory()) {
44367
+ throw new SkillError("INVALID_SOURCE", `Local skill source must be an existing directory: ${absoluteSource}`);
44368
+ }
44369
+ await verifySkillDirectory(absoluteSource);
44370
+ await (0, import_promises17.rm)(params.destination, { recursive: true, force: true });
44371
+ await (0, import_promises17.cp)(absoluteSource, params.destination, { recursive: true });
44372
+ }
44373
+ function deriveSkillName(request) {
44374
+ if (request.name) {
44375
+ return request.name;
44376
+ }
44377
+ const normalized = request.source.replace(/[\\/]+$/, "");
44378
+ const base = normalized.split(/[\\/]/).filter(Boolean).pop() ?? "";
44379
+ return base;
44380
+ }
44381
+ async function installSkill(params) {
44382
+ const validation = validateInstallRequest(params.request);
44383
+ if (!validation.ok) {
44384
+ throw new SkillError(validation.code, validation.error);
44385
+ }
44386
+ const skillName = params.request.name ?? deriveSkillName(params.request);
44387
+ if (!params.request.name) {
44388
+ const nameCheck = validateSkillName(skillName);
44389
+ if (!nameCheck.ok) {
44390
+ throw new SkillError(
44391
+ "INVALID_NAME",
44392
+ `Unable to derive a valid skill name from "${params.request.source}". Provide an explicit "name".`
44393
+ );
44394
+ }
44395
+ }
44396
+ const skillsDir = getSkillsDir(params.agentDir);
44397
+ const destination = (0, import_node_path21.join)(skillsDir, skillName);
44398
+ const existed = await dirExists(destination);
44399
+ if (existed && !params.request.overwrite) {
44400
+ throw new SkillError(
44401
+ "ALREADY_EXISTS",
44402
+ `Skill "${skillName}" already exists. Set overwrite=true to replace it.`
44403
+ );
44404
+ }
44405
+ await (0, import_promises17.mkdir)(skillsDir, { recursive: true });
44406
+ switch (params.request.sourceType) {
44407
+ case "local":
44408
+ await copyLocalSkill({
44409
+ source: params.request.source,
44410
+ cwd: params.cwd,
44411
+ destination
44412
+ });
44413
+ break;
44414
+ }
44415
+ const fmName = await readSkillFrontmatterName(destination);
44416
+ const computedHash = await computeSkillHash(destination);
44417
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
44418
+ const lock = await readSkillsLockFile(params.agentDir);
44419
+ lock.skills[skillName] = {
44420
+ source: params.request.source,
44421
+ sourceType: params.request.sourceType,
44422
+ computedHash,
44423
+ installedAt
44424
+ };
44425
+ await writeSkillsLockFile(params.agentDir, lock);
44426
+ return {
44427
+ name: fmName ?? skillName,
44428
+ directory: skillName,
44429
+ source: params.request.source,
44430
+ sourceType: params.request.sourceType,
44431
+ computedHash,
44432
+ installedAt,
44433
+ overwritten: existed
44434
+ };
44435
+ }
44436
+ async function uninstallSkill(params) {
44437
+ const nameCheck = validateSkillName(params.name);
44438
+ if (!nameCheck.ok) {
44439
+ throw new SkillError("INVALID_NAME", nameCheck.error);
44440
+ }
44441
+ const skillsDir = getSkillsDir(params.agentDir);
44442
+ let targetDir = params.name;
44443
+ const existed = await dirExists((0, import_node_path21.join)(skillsDir, targetDir));
44444
+ if (!existed) {
44445
+ const detail = await getSkillDetail(params.agentDir, params.name);
44446
+ if (!detail) {
44447
+ throw new SkillError("NOT_FOUND", `Skill "${params.name}" not found`);
44448
+ }
44449
+ targetDir = detail.skill.directory;
44450
+ }
44451
+ const destination = (0, import_node_path21.join)(skillsDir, targetDir);
44452
+ const lock = await readSkillsLockFile(params.agentDir);
44453
+ const lockKey = lock.skills[targetDir] ? targetDir : lock.skills[params.name] ? params.name : null;
44454
+ await (0, import_promises17.rm)(destination, { recursive: true, force: true });
44455
+ if (lockKey) {
44456
+ delete lock.skills[lockKey];
44457
+ await writeSkillsLockFile(params.agentDir, lock);
44458
+ }
44459
+ return { name: params.name, directory: targetDir, removed: true };
44460
+ }
43913
44461
 
43914
44462
  // src/server/toolsService.ts
43915
44463
  var BUILT_IN_TOOLS = {
@@ -44180,7 +44728,7 @@ var BROWSER_TOOL_SCHEMAS = [
44180
44728
  ];
44181
44729
 
44182
44730
  // src/server/sessionConfig.ts
44183
- var import_node_fs12 = require("node:fs");
44731
+ var import_node_fs13 = require("node:fs");
44184
44732
  var path5 = __toESM(require("node:path"));
44185
44733
  var DEFAULT_CONFIG = { autoCommit: false };
44186
44734
  function configPath(agentDir, sessionId) {
@@ -44188,7 +44736,7 @@ function configPath(agentDir, sessionId) {
44188
44736
  }
44189
44737
  async function readSessionConfig(agentDir, sessionId) {
44190
44738
  try {
44191
- const raw2 = await import_node_fs12.promises.readFile(configPath(agentDir, sessionId), "utf-8");
44739
+ const raw2 = await import_node_fs13.promises.readFile(configPath(agentDir, sessionId), "utf-8");
44192
44740
  return { ...DEFAULT_CONFIG, ...JSON.parse(raw2) };
44193
44741
  } catch {
44194
44742
  return { ...DEFAULT_CONFIG };
@@ -44196,14 +44744,28 @@ async function readSessionConfig(agentDir, sessionId) {
44196
44744
  }
44197
44745
  async function writeSessionConfig(agentDir, sessionId, patch) {
44198
44746
  const dir = path5.join(agentDir, "session-config");
44199
- await import_node_fs12.promises.mkdir(dir, { recursive: true });
44747
+ await import_node_fs13.promises.mkdir(dir, { recursive: true });
44200
44748
  const current = await readSessionConfig(agentDir, sessionId);
44201
44749
  const next = { ...current, ...patch };
44202
- await import_node_fs12.promises.writeFile(configPath(agentDir, sessionId), JSON.stringify(next, null, 2), "utf-8");
44750
+ await import_node_fs13.promises.writeFile(configPath(agentDir, sessionId), JSON.stringify(next, null, 2), "utf-8");
44203
44751
  return next;
44204
44752
  }
44205
44753
 
44206
44754
  // src/server/agentServer.ts
44755
+ var SKILL_ERROR_STATUS = {
44756
+ INVALID_REQUEST: 400,
44757
+ INVALID_NAME: 400,
44758
+ INVALID_SOURCE: 404,
44759
+ ALREADY_EXISTS: 409,
44760
+ NOT_FOUND: 404
44761
+ };
44762
+ function skillErrorResponse(c, error48) {
44763
+ if (error48 instanceof SkillError) {
44764
+ return c.json({ error: error48.message, code: error48.code }, SKILL_ERROR_STATUS[error48.code]);
44765
+ }
44766
+ const message = error48 instanceof Error ? error48.message : String(error48);
44767
+ return c.json({ error: message }, 500);
44768
+ }
44207
44769
  function generateMessageId() {
44208
44770
  return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
44209
44771
  }
@@ -44271,7 +44833,7 @@ async function waitForIdle(session, timeoutMs = 3e4) {
44271
44833
  if (!session.isStreaming) {
44272
44834
  return;
44273
44835
  }
44274
- return new Promise((resolve4, reject) => {
44836
+ return new Promise((resolve5, reject) => {
44275
44837
  const timer = setTimeout(() => {
44276
44838
  unsubscribe();
44277
44839
  reject(new Error("Timed out waiting for session to become idle"));
@@ -44280,13 +44842,13 @@ async function waitForIdle(session, timeoutMs = 3e4) {
44280
44842
  if (event.type === "agent_end") {
44281
44843
  clearTimeout(timer);
44282
44844
  unsubscribe();
44283
- resolve4();
44845
+ resolve5();
44284
44846
  }
44285
44847
  });
44286
44848
  if (!session.isStreaming) {
44287
44849
  clearTimeout(timer);
44288
44850
  unsubscribe();
44289
- resolve4();
44851
+ resolve5();
44290
44852
  }
44291
44853
  });
44292
44854
  }
@@ -45070,6 +45632,41 @@ async function createAgentServer(params) {
45070
45632
  commands: normalizeSlashCommands(activeSession.listCommands())
45071
45633
  });
45072
45634
  });
45635
+ app.get("/api/souls", async (c) => {
45636
+ const configFilePath = (0, import_node_path22.join)(settingsRootPath, "config.json");
45637
+ const envConfigService = new EnvironmentConfigService(configFilePath);
45638
+ const activeSoulId = await envConfigService.getActiveSoulId();
45639
+ const runtimeSoulId = process.env.DOCYRUS_PI_SOUL_ID?.trim() || DEFAULT_SOUL_ID;
45640
+ return c.json({
45641
+ souls: listSouls(activeSoulId),
45642
+ activeSoulId,
45643
+ runtimeSoulId,
45644
+ restartRequired: activeSoulId !== runtimeSoulId
45645
+ });
45646
+ });
45647
+ app.post("/api/souls/select", async (c) => {
45648
+ const body2 = await c.req.json().catch(() => ({}));
45649
+ const requested = typeof body2.soulId === "string" ? body2.soulId.trim() : "";
45650
+ if (!requested) {
45651
+ return c.json({ error: "Field 'soulId' is required." }, 400);
45652
+ }
45653
+ if (!SOULS.some((soul) => soul.id === requested)) {
45654
+ return c.json({
45655
+ error: `Unknown soul '${requested}'.`,
45656
+ availableSoulIds: SOULS.map((soul) => soul.id)
45657
+ }, 400);
45658
+ }
45659
+ const configFilePath = (0, import_node_path22.join)(settingsRootPath, "config.json");
45660
+ const envConfigService = new EnvironmentConfigService(configFilePath);
45661
+ const persistedSoulId = await envConfigService.setActiveSoulId(requested);
45662
+ const runtimeSoulId = process.env.DOCYRUS_PI_SOUL_ID?.trim() || DEFAULT_SOUL_ID;
45663
+ return c.json({
45664
+ soulId: persistedSoulId,
45665
+ runtimeSoulId,
45666
+ restartRequired: persistedSoulId !== runtimeSoulId,
45667
+ message: persistedSoulId === runtimeSoulId ? "Soul persisted. Already running \u2014 no restart needed." : "Soul persisted. Restart the agent (or open a new session) for the new soul to take effect."
45668
+ });
45669
+ });
45073
45670
  app.get("/api/knowledge/sections", async (c) => {
45074
45671
  try {
45075
45672
  const query = c.req.query("query")?.trim();
@@ -46420,6 +47017,69 @@ async function createAgentServer(params) {
46420
47017
  return c.json({ error: message }, status);
46421
47018
  }
46422
47019
  });
47020
+ app.get("/api/mcp/settings", async (c) => {
47021
+ try {
47022
+ const config2 = await loadMcpServerConfig(context.agentDir, context.cwd);
47023
+ return c.json({
47024
+ settings: {
47025
+ toolPrefix: config2.settings?.toolPrefix ?? "server",
47026
+ idleTimeout: config2.settings?.idleTimeout ?? 10,
47027
+ directTools: config2.settings?.directTools ?? false
47028
+ },
47029
+ supportedToolPrefixes: ["server", "none", "short"]
47030
+ });
47031
+ } catch (error48) {
47032
+ const message = error48 instanceof Error ? error48.message : String(error48);
47033
+ return c.json({ error: message }, 500);
47034
+ }
47035
+ });
47036
+ app.put("/api/mcp/settings", async (c) => {
47037
+ try {
47038
+ const body2 = await c.req.json();
47039
+ const result = validateMcpSettings(body2.settings);
47040
+ if (!result.ok) {
47041
+ return c.json({ error: result.error }, 400);
47042
+ }
47043
+ const merged = await updateMcpSettings(context.agentDir, result.value);
47044
+ return c.json({
47045
+ settings: {
47046
+ toolPrefix: merged.toolPrefix ?? "server",
47047
+ idleTimeout: merged.idleTimeout ?? 10,
47048
+ directTools: merged.directTools ?? false
47049
+ }
47050
+ });
47051
+ } catch (error48) {
47052
+ const message = error48 instanceof Error ? error48.message : String(error48);
47053
+ return c.json({ error: message }, 500);
47054
+ }
47055
+ });
47056
+ app.get("/api/mcp/imports", async (c) => {
47057
+ try {
47058
+ const summary = await describeImports(context.agentDir, context.cwd);
47059
+ return c.json({
47060
+ enabled: summary.enabled,
47061
+ available: summary.available,
47062
+ supported: SUPPORTED_IMPORT_KINDS
47063
+ });
47064
+ } catch (error48) {
47065
+ const message = error48 instanceof Error ? error48.message : String(error48);
47066
+ return c.json({ error: message }, 500);
47067
+ }
47068
+ });
47069
+ app.put("/api/mcp/imports", async (c) => {
47070
+ try {
47071
+ const body2 = await c.req.json();
47072
+ const result = validateImportKinds(body2.imports);
47073
+ if (!result.ok) {
47074
+ return c.json({ error: result.error }, 400);
47075
+ }
47076
+ const imports = await updateMcpImports(context.agentDir, result.value);
47077
+ return c.json({ imports });
47078
+ } catch (error48) {
47079
+ const message = error48 instanceof Error ? error48.message : String(error48);
47080
+ return c.json({ error: message }, 500);
47081
+ }
47082
+ });
46423
47083
  app.get("/api/skills", async (c) => {
46424
47084
  try {
46425
47085
  const skills = (await listSkills(context.agentDir)).filter((skill) => skill.name !== "docyrus-chrome-devtools-cli");
@@ -46445,6 +47105,56 @@ async function createAgentServer(params) {
46445
47105
  return c.json({ error: message }, 500);
46446
47106
  }
46447
47107
  });
47108
+ app.get("/api/skills/lock/entries", async (c) => {
47109
+ try {
47110
+ const lock = await readSkillsLockFile(context.agentDir);
47111
+ return c.json({
47112
+ version: lock.version,
47113
+ skills: lock.skills,
47114
+ supportedSourceTypes: ["local"]
47115
+ });
47116
+ } catch (error48) {
47117
+ const message = error48 instanceof Error ? error48.message : String(error48);
47118
+ return c.json({ error: message }, 500);
47119
+ }
47120
+ });
47121
+ app.post("/api/skills", async (c) => {
47122
+ try {
47123
+ const body2 = await c.req.json();
47124
+ if (!body2.source || typeof body2.source !== "string") {
47125
+ return c.json({ error: "Missing required field: source" }, 400);
47126
+ }
47127
+ if (!body2.sourceType || typeof body2.sourceType !== "string") {
47128
+ return c.json({ error: "Missing required field: sourceType" }, 400);
47129
+ }
47130
+ const request = {
47131
+ source: body2.source,
47132
+ sourceType: body2.sourceType,
47133
+ name: body2.name,
47134
+ overwrite: body2.overwrite === true
47135
+ };
47136
+ const result = await installSkill({
47137
+ agentDir: context.agentDir,
47138
+ cwd: context.cwd,
47139
+ request
47140
+ });
47141
+ return c.json({ ok: true, skill: result }, result.overwritten ? 200 : 201);
47142
+ } catch (error48) {
47143
+ return skillErrorResponse(c, error48);
47144
+ }
47145
+ });
47146
+ app.delete("/api/skills/:skillName", async (c) => {
47147
+ const skillName = c.req.param("skillName");
47148
+ try {
47149
+ const result = await uninstallSkill({
47150
+ agentDir: context.agentDir,
47151
+ name: skillName
47152
+ });
47153
+ return c.json({ ok: true, ...result });
47154
+ } catch (error48) {
47155
+ return skillErrorResponse(c, error48);
47156
+ }
47157
+ });
46448
47158
  app.post("/api/extension-ui-response", async (c) => {
46449
47159
  if (!extensionUIBridge) {
46450
47160
  return c.json({ error: "Extension UI bridge not available" }, 404);
@@ -46692,10 +47402,24 @@ async function createAgentServer(params) {
46692
47402
  process.stderr.write(` PUT /api/mcp/servers/:name \u2014 update MCP server
46693
47403
  `);
46694
47404
  process.stderr.write(` DEL /api/mcp/servers/:name \u2014 remove MCP server
47405
+ `);
47406
+ process.stderr.write(` GET /api/mcp/settings \u2014 MCP global settings
47407
+ `);
47408
+ process.stderr.write(` PUT /api/mcp/settings \u2014 update MCP global settings
47409
+ `);
47410
+ process.stderr.write(` GET /api/mcp/imports \u2014 available and enabled MCP imports
47411
+ `);
47412
+ process.stderr.write(` PUT /api/mcp/imports \u2014 update enabled MCP imports
46695
47413
  `);
46696
47414
  process.stderr.write(` GET /api/skills \u2014 list skills
46697
47415
  `);
46698
47416
  process.stderr.write(` GET /api/skills/:name \u2014 skill details
47417
+ `);
47418
+ process.stderr.write(` POST /api/skills \u2014 install a skill from local source
47419
+ `);
47420
+ process.stderr.write(` DEL /api/skills/:name \u2014 uninstall a skill
47421
+ `);
47422
+ process.stderr.write(` GET /api/skills/lock/entries \u2014 skills-lock.json entries
46699
47423
  `);
46700
47424
  process.stderr.write(` GET /api/tools \u2014 list all tools
46701
47425
  `);
@@ -46756,7 +47480,7 @@ function extractBearerToken(authorizationHeader) {
46756
47480
  return token;
46757
47481
  }
46758
47482
  function hashToken(token) {
46759
- return (0, import_node_crypto6.createHash)("sha256").update(token).digest();
47483
+ return (0, import_node_crypto7.createHash)("sha256").update(token).digest();
46760
47484
  }
46761
47485
  function isBearerTokenAuthorized(actualToken, expectedToken) {
46762
47486
  if (!expectedToken) {
@@ -46767,7 +47491,7 @@ function isBearerTokenAuthorized(actualToken, expectedToken) {
46767
47491
  }
46768
47492
  const actualHash = hashToken(actualToken);
46769
47493
  const expectedHash = hashToken(expectedToken);
46770
- return (0, import_node_crypto6.timingSafeEqual)(actualHash, expectedHash);
47494
+ return (0, import_node_crypto7.timingSafeEqual)(actualHash, expectedHash);
46771
47495
  }
46772
47496
  function createUnauthorizedResponse() {
46773
47497
  return new Response(JSON.stringify({ error: "Unauthorized" }), {
@@ -46901,7 +47625,7 @@ function createServerSessionAdapter(params) {
46901
47625
  }
46902
47626
 
46903
47627
  // src/server/extensionUIBridge.ts
46904
- var import_node_crypto7 = require("node:crypto");
47628
+ var import_node_crypto8 = require("node:crypto");
46905
47629
  var ExtensionUIBridge = class {
46906
47630
  pending = /* @__PURE__ */ new Map();
46907
47631
  requestHandler;
@@ -46945,9 +47669,9 @@ var ExtensionUIBridge = class {
46945
47669
  if (opts?.signal?.aborted) {
46946
47670
  return Promise.resolve(defaultValue);
46947
47671
  }
46948
- const id = (0, import_node_crypto7.randomUUID)();
47672
+ const id = (0, import_node_crypto8.randomUUID)();
46949
47673
  const timeout = opts?.timeout ?? this.defaultTimeout;
46950
- return new Promise((resolve4, reject) => {
47674
+ return new Promise((resolve5, reject) => {
46951
47675
  const cleanup = () => {
46952
47676
  if (timer) {
46953
47677
  clearTimeout(timer);
@@ -46957,17 +47681,17 @@ var ExtensionUIBridge = class {
46957
47681
  };
46958
47682
  const onAbort = () => {
46959
47683
  cleanup();
46960
- resolve4(defaultValue);
47684
+ resolve5(defaultValue);
46961
47685
  };
46962
47686
  opts?.signal?.addEventListener("abort", onAbort, { once: true });
46963
47687
  const timer = timeout > 0 ? setTimeout(() => {
46964
47688
  cleanup();
46965
- resolve4(defaultValue);
47689
+ resolve5(defaultValue);
46966
47690
  }, timeout) : void 0;
46967
47691
  this.pending.set(id, {
46968
47692
  resolve: (response) => {
46969
47693
  cleanup();
46970
- resolve4(parseResponse(response));
47694
+ resolve5(parseResponse(response));
46971
47695
  },
46972
47696
  reject: (error48) => {
46973
47697
  cleanup();
@@ -47008,23 +47732,23 @@ var ExtensionUIBridge = class {
47008
47732
  opts
47009
47733
  ),
47010
47734
  notify: (message, notifyType) => {
47011
- const id = (0, import_node_crypto7.randomUUID)();
47735
+ const id = (0, import_node_crypto8.randomUUID)();
47012
47736
  this.requestHandler({ type: "extension_ui_request", id, method: "notify", message, placeholder: notifyType });
47013
47737
  },
47014
47738
  setStatus: (statusKey, statusText) => {
47015
- const id = (0, import_node_crypto7.randomUUID)();
47739
+ const id = (0, import_node_crypto8.randomUUID)();
47016
47740
  this.requestHandler({ type: "extension_ui_request", id, method: "setStatus", title: statusKey, message: statusText });
47017
47741
  },
47018
47742
  setWidget: (widgetKey, widgetLines) => {
47019
- const id = (0, import_node_crypto7.randomUUID)();
47743
+ const id = (0, import_node_crypto8.randomUUID)();
47020
47744
  this.requestHandler({ type: "extension_ui_request", id, method: "setWidget", title: widgetKey, options: widgetLines });
47021
47745
  },
47022
47746
  setTitle: (title) => {
47023
- const id = (0, import_node_crypto7.randomUUID)();
47747
+ const id = (0, import_node_crypto8.randomUUID)();
47024
47748
  this.requestHandler({ type: "extension_ui_request", id, method: "setTitle", title });
47025
47749
  },
47026
47750
  setEditorText: (text3) => {
47027
- const id = (0, import_node_crypto7.randomUUID)();
47751
+ const id = (0, import_node_crypto8.randomUUID)();
47028
47752
  this.requestHandler({ type: "extension_ui_request", id, method: "set_editor_text", message: text3 });
47029
47753
  },
47030
47754
  // No-op methods for server mode (not applicable without TUI)
@@ -47105,7 +47829,7 @@ function resolvePackagedPiResourceRoot() {
47105
47829
  (0, import_node_path23.resolve)(__dirname, "resources/pi-agent"),
47106
47830
  (0, import_node_path23.resolve)(process.cwd(), "dist/apps/api-cli/resources/pi-agent")
47107
47831
  ];
47108
- const resolved = candidates.find((candidate) => (0, import_node_fs13.existsSync)(candidate));
47832
+ const resolved = candidates.find((candidate) => (0, import_node_fs14.existsSync)(candidate));
47109
47833
  if (!resolved) {
47110
47834
  throw new Error(`Unable to locate pi agent resources. Checked: ${candidates.join(", ")}`);
47111
47835
  }
@@ -47123,25 +47847,11 @@ function setProcessArgValue(flag, value2) {
47123
47847
  }
47124
47848
  process.argv.push(flag, value2);
47125
47849
  }
47126
- function buildTools(profile, cwd, pi) {
47850
+ function buildTools(profile) {
47127
47851
  if (profile === "agent") {
47128
- return [
47129
- pi.createReadTool(cwd),
47130
- pi.createBashTool(cwd),
47131
- pi.createGrepTool(cwd),
47132
- pi.createFindTool(cwd),
47133
- pi.createLsTool(cwd)
47134
- ];
47852
+ return ["read", "bash", "grep", "find", "ls"];
47135
47853
  }
47136
- return [
47137
- pi.createReadTool(cwd),
47138
- pi.createBashTool(cwd),
47139
- pi.createEditTool(cwd),
47140
- pi.createWriteTool(cwd),
47141
- pi.createGrepTool(cwd),
47142
- pi.createFindTool(cwd),
47143
- pi.createLsTool(cwd)
47144
- ];
47854
+ return ["read", "bash", "edit", "write", "grep", "find", "ls"];
47145
47855
  }
47146
47856
  function resolveRequestedModel(params) {
47147
47857
  const { request, modelRegistry } = params;
@@ -47237,7 +47947,7 @@ async function main() {
47237
47947
  skills: skills.filter((skill) => skill.name !== "docyrus-chrome-devtools-cli"),
47238
47948
  diagnostics
47239
47949
  }),
47240
- systemPrompt: (0, import_node_path23.join)(
47950
+ systemPrompt: process.env.DOCYRUS_PI_SYSTEM_PROMPT_PATH ?? (0, import_node_path23.join)(
47241
47951
  resourceRoot,
47242
47952
  "prompts",
47243
47953
  request.profile === "agent" ? "agent-system.md" : "coder-system.md"
@@ -47253,7 +47963,7 @@ async function main() {
47253
47963
  resourceLoader,
47254
47964
  settingsManager,
47255
47965
  sessionManager,
47256
- tools: buildTools(request.profile, cwd, pi),
47966
+ tools: buildTools(request.profile),
47257
47967
  model: requestedModel,
47258
47968
  thinkingLevel: request.thinking
47259
47969
  });
@@ -47313,7 +48023,7 @@ async function main() {
47313
48023
  resourceLoader,
47314
48024
  settingsManager,
47315
48025
  sessionManager,
47316
- tools: buildTools(request.profile, cwd, pi),
48026
+ tools: buildTools(request.profile),
47317
48027
  model: requestedModel,
47318
48028
  thinkingLevel: request.thinking
47319
48029
  });
@@ -47348,7 +48058,7 @@ async function main() {
47348
48058
  resourceLoader,
47349
48059
  settingsManager,
47350
48060
  sessionManager: resumedSessionManager,
47351
- tools: buildTools(request.profile, resumeCwd, pi),
48061
+ tools: buildTools(request.profile),
47352
48062
  model: requestedModel,
47353
48063
  thinkingLevel: request.thinking
47354
48064
  });