cmdr-mcp 0.4.0 → 0.6.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 (38) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/README.md +44 -17
  3. package/docs/README.zh-CN.md +33 -8
  4. package/docs/agent-integration.md +74 -12
  5. package/docs/dashboard-plan.md +327 -0
  6. package/docs/dashboard.md +101 -0
  7. package/docs/implementation.md +25 -4
  8. package/docs/long-running-collaboration.md +4 -2
  9. package/docs/publishing.md +11 -9
  10. package/docs/setup.md +4 -3
  11. package/docs/troubleshooting.md +3 -3
  12. package/marketplace.json +2 -2
  13. package/package.json +10 -2
  14. package/plugins/cmdr/.claude-plugin/plugin.json +1 -1
  15. package/plugins/cmdr/.codex-plugin/plugin.json +1 -1
  16. package/plugins/cmdr/.kimi-plugin/plugin.json +65 -0
  17. package/plugins/cmdr/.zcode-plugin/plugin.json +1 -1
  18. package/plugins/cmdr/README.md +7 -5
  19. package/plugins/cmdr/THIRD_PARTY_NOTICES.txt +130 -0
  20. package/plugins/cmdr/bin/cmdr-check.mjs +6 -1
  21. package/plugins/cmdr/bin/cmdr-node +8 -1
  22. package/plugins/cmdr/commands/cmdr.md +3 -1
  23. package/plugins/cmdr/dist/cli.mjs +226 -43
  24. package/plugins/cmdr/dist/daemon.mjs +4792 -161
  25. package/plugins/cmdr/dist/dashboard/app.css +1 -0
  26. package/plugins/cmdr/dist/dashboard/app.js +10 -0
  27. package/plugins/cmdr/dist/dashboard/index.html +13 -0
  28. package/plugins/cmdr/dist/hook.mjs +22 -4
  29. package/plugins/cmdr/dist/integrity.json +24 -19
  30. package/plugins/cmdr/dist/mcp.mjs +148 -66
  31. package/plugins/cmdr/kimi-identity/SKILL.md +12 -0
  32. package/plugins/cmdr/skills/cmdr/SKILL.md +4 -2
  33. package/plugins/cmdr/skills/cmdr/references/commander.md +3 -1
  34. package/plugins/cmdr/skills/cmdr/references/executor.md +3 -1
  35. package/plugins/cmdr/skills/cmdr/references/setup.md +1 -1
  36. package/plugins/cmdr/skills/cmdr-commander/SKILL.md +3 -1
  37. package/plugins/cmdr/skills/cmdr-executor/SKILL.md +3 -1
  38. package/plugins/cmdr/skills/using-cmdr/SKILL.md +3 -3
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>cmdr</title>
7
+ <link rel="stylesheet" href="/app.css" />
8
+ <script type="module" src="/app.js"></script>
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
@@ -85,10 +85,11 @@ function observedHook(event, agent) {
85
85
  import { existsSync } from "node:fs";
86
86
 
87
87
  // src/shared/env.ts
88
- var cmdrTool = /(?:^|[_:])cmdr(?:__|:)(list|join|report|leave|ask|send|read)$/;
88
+ var cmdrTool = /(?:^|[_:])cmdr(?:__|:)(list|join|report|leave|ask|send|read|task|artifact)$/;
89
89
  function detectAgent(env = process.env, hook) {
90
90
  if (env.CMDR_AGENT && /^[a-z][a-z0-9_-]{0,63}$/.test(env.CMDR_AGENT)) return env.CMDR_AGENT;
91
91
  if (env.ZCODE_PLUGIN_ROOT || env.ZCODE_PLUGIN_ID) return "zcode";
92
+ if (env.KIMI_PLUGIN_ROOT) return "kimi";
92
93
  if (env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_CODE_ENTRYPOINT) return "claude";
93
94
  if (hook?.transcript_path && String(hook.transcript_path).includes("/.claude/")) return "claude";
94
95
  if (env.CODEX_HOME || env.CODEX_THREAD_ID || env.CODEX_SESSION_ID || hook?.turn_id)
@@ -254,7 +255,7 @@ var Rpc = class extends EventEmitter {
254
255
  };
255
256
 
256
257
  // src/shared/version.ts
257
- var VERSION = true ? "0.4.0" : MIN_CLIENT_VERSION;
258
+ var VERSION = true ? "0.6.0" : MIN_CLIENT_VERSION;
258
259
  var PROTOCOL = 1;
259
260
  function newer(a, b) {
260
261
  const x = a.split(".").map(Number), y = b.split(".").map(Number);
@@ -391,6 +392,13 @@ async function runHook(input, event = input.hook_event_name) {
391
392
  return;
392
393
  }
393
394
  if (event === "PreToolUse" && cmdrTool.test(input.tool_name || "")) {
395
+ if (agent === "kimi") {
396
+ if ((input.tool_input || {})._cmdr_session === input.session_id) return;
397
+ return {
398
+ decision: "block",
399
+ reason: `[cmdr] Kimi Code shares one cmdr MCP process per workspace; retry this call with _cmdr_session="${input.session_id}".`
400
+ };
401
+ }
394
402
  if (agent !== "claude")
395
403
  return {
396
404
  hookSpecificOutput: {
@@ -424,8 +432,18 @@ try {
424
432
  input += chunk;
425
433
  if (input.length > 2 * 1024 * 1024) throw new Error("large input");
426
434
  }
427
- const result = await runHook(JSON.parse(input), process.argv[2]);
428
- if (result) process.stdout.write(JSON.stringify(result) + "\n");
435
+ const parsed = JSON.parse(input);
436
+ const agent = detectAgent(process.env, parsed);
437
+ const result = await runHook(parsed, process.argv[2]);
438
+ if (result) {
439
+ if (agent === "kimi") {
440
+ if (result.decision === "block")
441
+ process.stderr.write(`${result.reason}
442
+ `, () => process.exit(2));
443
+ else if (process.argv[2] === "UserPromptSubmit" && result.hookSpecificOutput?.additionalContext)
444
+ process.stdout.write(result.hookSpecificOutput.additionalContext + "\n");
445
+ } else process.stdout.write(JSON.stringify(result) + "\n");
446
+ }
429
447
  } catch (e) {
430
448
  diagnostic(e.code === "DAEMON_UNAVAILABLE" ? "hook-unavailable" : "hook-error");
431
449
  }
@@ -1,29 +1,34 @@
1
1
  {
2
- "version": "0.4.0",
2
+ "version": "0.6.0",
3
3
  "files": {
4
- ".claude-plugin/plugin.json": "6d9ae2514ca252e356ebf46ab54b2a9aa3599ae3b3630f89aba16721e73be055",
5
- ".codex-plugin/plugin.json": "7dc3738401bb0a78758a5ac906aebba1d74883c4d34a757ae84093b35bc8e7fa",
4
+ ".claude-plugin/plugin.json": "03daaf06535131d19dc7444907f1af1100a9deeb2b07553862805c33d49f878d",
5
+ ".codex-plugin/plugin.json": "36b3066ada5434eda049904b6a90ef867f093a3f861e6a61e9df7fddb5963156",
6
+ ".kimi-plugin/plugin.json": "7634842fb15abaf7a6ccc9d4500cd27447515e3d6c99de0a3249bf0f74631bc6",
6
7
  ".mcp.json": "34e310a2874c8fda625745672a54df19614f32dabd2be4013f8ce871260e88c4",
7
- ".zcode-plugin/plugin.json": "c30588e54d76fee931aaf329637cf004cc4f19155a21a3061d9d1707bc039313",
8
- "THIRD_PARTY_NOTICES.txt": "acad9112a321e2c4a630e422c3aa78eb7d40093548bf476d6bd0133ea45dd52d",
8
+ ".zcode-plugin/plugin.json": "6a5f67567958487a9a10db5bf879641de790c5734fafaeab7ce39a978ddab43a",
9
+ "THIRD_PARTY_NOTICES.txt": "061121858596143e1ec0609aeee0d45ec839e53f05a4b7c23a48ec7df33194fc",
9
10
  "bin/cmdr": "be6ea6b84ce121738ca729e9c6dbfb87bbf349a84d251acc711e3ea11f8dd8fd",
10
- "bin/cmdr-check.mjs": "6192d158606b253637635416e706f8ba782b8e8d63c1613c76f5011c09a58a85",
11
+ "bin/cmdr-check.mjs": "669204363e26323af6abd623e9039c2c7e9ca9626ee9418e9ab9e2a85be7fc67",
11
12
  "bin/cmdr-daemon": "7f8ed3a755f975751ddda077242adf661834dddbb8be7d3a88caee0c70cce5db",
12
13
  "bin/cmdr-hook": "e1fa5950325775e467e4e01aeff3c5cb8c1dba35c4535d7cb569f658f95e7f15",
13
14
  "bin/cmdr-mcp": "356bea13c2b82075c09fd663106791edab9e06fdb04fc679a98de33514b10a83",
14
- "bin/cmdr-node": "ce5952648b9a7deccdd855808331d51ff8569675100278379ef1fc84cd3d528e",
15
- "commands/cmdr.md": "e13c9b8d6ec9518c277b83ae0794009bdb354d21e6e7ce42e9ada1bdfeb8de85",
16
- "dist/cli.mjs": "b7498f59eedcfb1bf312cd8bcddddbd6d24c0278f1bc0617c1d6192da4f703dc",
17
- "dist/daemon.mjs": "d67b67d5cc27ce7e445cd5a05abed82842f502be646aaa971124e82f399ea5d1",
18
- "dist/hook.mjs": "fabbfd17629992d9f31575921c61668aebfe79a94e9b4bc5aeed1fe92d849053",
19
- "dist/mcp.mjs": "a23acd329ad5f10c55a4edfc378294e27b480d2b7401215d6e1bd6ca2f5e4dd3",
15
+ "bin/cmdr-node": "d4005e39ec1edc05a45413f38172a239a234cc457fd17a320db5e24bdbc2e6c8",
16
+ "commands/cmdr.md": "c46d1d3d38af10ac75f8793b998e0889a9136da82b883816a724502689184e49",
17
+ "dist/cli.mjs": "99ffbb22e10223c012100896ed7fe571fe0e1095c34003f54fdc9b389987cd22",
18
+ "dist/daemon.mjs": "21335e66c4eb808e8f9e8d6c6de0daf3a3e733616eb4c89547c7621a9e51c690",
19
+ "dist/dashboard/app.css": "58d74d9a98c39bef501a53f2530a99906d2fbb8ef5a619ff7a8ee432005d65d3",
20
+ "dist/dashboard/app.js": "c3549fce634931de741df6883e63e246969eedfedd8af4e81e7afe0687a10d1e",
21
+ "dist/dashboard/index.html": "670b1a05ca906b352ed5d8f53d2adc1a9ad5bb937e758a09ae200752f4e25356",
22
+ "dist/hook.mjs": "ce32d8fda6272f9cbfe0fb8bde9d68ce55a10c3ceacb6f3166787115df96e793",
23
+ "dist/mcp.mjs": "55950f4e6be2600881e8ca9c89e316b41a2039c98278199b8c846900869e41ba",
20
24
  "hooks/hooks.json": "6ff937910fc04eb81db4a84dfeb91a92ab971364d684a45a85f3a9953eb21259",
21
- "skills/cmdr/SKILL.md": "afb3a67d00f0e7253448395d88d3743121cfb75f4316674456d7efdb11555164",
22
- "skills/cmdr/references/commander.md": "69ba658f21ac07a7d254e11d6a66ef06e3d56e1e17b6eb49f349257ccf0026e2",
23
- "skills/cmdr/references/executor.md": "361db0b48b2151eab56d7cb1d501ff547a03ba379081f36345cfd27741da0225",
24
- "skills/cmdr/references/setup.md": "2ba2db967820ee2ded81730a5ff02bf7306dd8d6c1480818bc8223210392be97",
25
- "skills/cmdr-commander/SKILL.md": "e29e3e49392f603e87e61d8d54a93a07ed8e643bea7016e1e69809d58733a77d",
26
- "skills/cmdr-executor/SKILL.md": "acaf559c9d49bb86d6bb6c4bc5f5100859f62babbde5ce4ec0f9d2b06fa3c176",
27
- "skills/using-cmdr/SKILL.md": "fb52c602a81e2f8582cb91fabb7a362345ede2ea4f4cb10894023dc375bfcf1e"
25
+ "kimi-identity/SKILL.md": "967454ed3a9c1ddeeb87c5f419ce6303399caec1563192777c564445e6e6d59f",
26
+ "skills/cmdr/SKILL.md": "f95875b0b54877547a16158403dbdf7162c9a10d417dc956907acd81fd174305",
27
+ "skills/cmdr/references/commander.md": "48728f778a53cd2ae9d38cafcfa5f6c00f9b92a29acad452eecba1c07d04985d",
28
+ "skills/cmdr/references/executor.md": "c7527c2a15cb593407b9547dd431bc499d6eaa7216e8fae5c14402c6c748b9ad",
29
+ "skills/cmdr/references/setup.md": "8049617af8d441a586e498a8c6fb6bd84836b5d8ffd7199c4cec2b589fdcf4ad",
30
+ "skills/cmdr-commander/SKILL.md": "2b39799de58cef778d2009acede67701e2ec3fef5e0e617480a79d048fc0a692",
31
+ "skills/cmdr-executor/SKILL.md": "dad00e3f9116b19d0009bbeb9f2527e0ffcbce20c7b70ab5208656e091bfbd14",
32
+ "skills/using-cmdr/SKILL.md": "921ac245e5884cf4e5bf9a07520b186ebdf47c0cfc93e6e171c4072d163e03fd"
28
33
  }
29
34
  }
@@ -2232,10 +2232,10 @@ var require_resolve = __commonJS({
2232
2232
  }
2233
2233
  return count;
2234
2234
  }
2235
- function getFullPath(resolver, id = "", normalize) {
2235
+ function getFullPath(resolver, id2 = "", normalize) {
2236
2236
  if (normalize !== false)
2237
- id = normalizeId(id);
2238
- const p = resolver.parse(id);
2237
+ id2 = normalizeId(id2);
2238
+ const p = resolver.parse(id2);
2239
2239
  return _getFullPath(resolver, p);
2240
2240
  }
2241
2241
  exports.getFullPath = getFullPath;
@@ -2245,13 +2245,13 @@ var require_resolve = __commonJS({
2245
2245
  }
2246
2246
  exports._getFullPath = _getFullPath;
2247
2247
  var TRAILING_SLASH_HASH = /#\/?$/;
2248
- function normalizeId(id) {
2249
- return id ? id.replace(TRAILING_SLASH_HASH, "") : "";
2248
+ function normalizeId(id2) {
2249
+ return id2 ? id2.replace(TRAILING_SLASH_HASH, "") : "";
2250
2250
  }
2251
2251
  exports.normalizeId = normalizeId;
2252
- function resolveUrl(resolver, baseId, id) {
2253
- id = normalizeId(id);
2254
- return resolver.resolve(baseId, id);
2252
+ function resolveUrl(resolver, baseId, id2) {
2253
+ id2 = normalizeId(id2);
2254
+ return resolver.resolve(baseId, id2);
2255
2255
  }
2256
2256
  exports.resolveUrl = resolveUrl;
2257
2257
  var ANCHOR = /^[a-z_][-a-z0-9._]*$/i;
@@ -3021,8 +3021,8 @@ var require_compile = __commonJS({
3021
3021
  if (Object.keys(root.schema).length > 0 && refPath === baseId) {
3022
3022
  return getJsonPointer.call(this, p, root);
3023
3023
  }
3024
- const id = (0, resolve_1.normalizeId)(refPath);
3025
- const schOrRef = this.refs[id] || this.schemas[id];
3024
+ const id2 = (0, resolve_1.normalizeId)(refPath);
3025
+ const schOrRef = this.refs[id2] || this.schemas[id2];
3026
3026
  if (typeof schOrRef == "string") {
3027
3027
  const sch = resolveSchema.call(this, root, schOrRef);
3028
3028
  if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object")
@@ -3033,7 +3033,7 @@ var require_compile = __commonJS({
3033
3033
  return;
3034
3034
  if (!schOrRef.validate)
3035
3035
  compileSchema.call(this, schOrRef);
3036
- if (id === (0, resolve_1.normalizeId)(ref)) {
3036
+ if (id2 === (0, resolve_1.normalizeId)(ref)) {
3037
3037
  const { schema } = schOrRef;
3038
3038
  const { schemaId } = this.opts;
3039
3039
  const schId = schema[schemaId];
@@ -4459,15 +4459,15 @@ var require_core = __commonJS({
4459
4459
  this.addSchema(sch, void 0, _meta, _validateSchema);
4460
4460
  return this;
4461
4461
  }
4462
- let id;
4462
+ let id2;
4463
4463
  if (typeof schema === "object") {
4464
4464
  const { schemaId } = this.opts;
4465
- id = schema[schemaId];
4466
- if (id !== void 0 && typeof id != "string") {
4465
+ id2 = schema[schemaId];
4466
+ if (id2 !== void 0 && typeof id2 != "string") {
4467
4467
  throw new Error(`schema ${schemaId} must be string`);
4468
4468
  }
4469
4469
  }
4470
- key = (0, resolve_1.normalizeId)(key || id);
4470
+ key = (0, resolve_1.normalizeId)(key || id2);
4471
4471
  this._checkUnique(key);
4472
4472
  this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true);
4473
4473
  return this;
@@ -4546,11 +4546,11 @@ var require_core = __commonJS({
4546
4546
  case "object": {
4547
4547
  const cacheKey = schemaKeyRef;
4548
4548
  this._cache.delete(cacheKey);
4549
- let id = schemaKeyRef[this.opts.schemaId];
4550
- if (id) {
4551
- id = (0, resolve_1.normalizeId)(id);
4552
- delete this.schemas[id];
4553
- delete this.refs[id];
4549
+ let id2 = schemaKeyRef[this.opts.schemaId];
4550
+ if (id2) {
4551
+ id2 = (0, resolve_1.normalizeId)(id2);
4552
+ delete this.schemas[id2];
4553
+ delete this.refs[id2];
4554
4554
  }
4555
4555
  return this;
4556
4556
  }
@@ -4621,7 +4621,7 @@ var require_core = __commonJS({
4621
4621
  errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
4622
4622
  if (!errors || errors.length === 0)
4623
4623
  return "No errors";
4624
- return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text2, msg) => text2 + separator + msg);
4624
+ return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text3, msg) => text3 + separator + msg);
4625
4625
  }
4626
4626
  $dataMetaSchema(metaSchema, keywordsJsonPointers) {
4627
4627
  const rules = this.RULES.all;
@@ -4657,10 +4657,10 @@ var require_core = __commonJS({
4657
4657
  }
4658
4658
  }
4659
4659
  _addSchema(schema, meta, baseId, validateSchema = this.opts.validateSchema, addSchema = this.opts.addUsedSchema) {
4660
- let id;
4660
+ let id2;
4661
4661
  const { schemaId } = this.opts;
4662
4662
  if (typeof schema == "object") {
4663
- id = schema[schemaId];
4663
+ id2 = schema[schemaId];
4664
4664
  } else {
4665
4665
  if (this.opts.jtd)
4666
4666
  throw new Error("schema must be object");
@@ -4670,7 +4670,7 @@ var require_core = __commonJS({
4670
4670
  let sch = this._cache.get(schema);
4671
4671
  if (sch !== void 0)
4672
4672
  return sch;
4673
- baseId = (0, resolve_1.normalizeId)(id || baseId);
4673
+ baseId = (0, resolve_1.normalizeId)(id2 || baseId);
4674
4674
  const localRefs = resolve_1.getSchemaRefs.call(this, schema, baseId);
4675
4675
  sch = new compile_1.SchemaEnv({ schema, schemaId, meta, baseId, localRefs });
4676
4676
  this._cache.set(sch.schema, sch);
@@ -4683,9 +4683,9 @@ var require_core = __commonJS({
4683
4683
  this.validateSchema(schema, true);
4684
4684
  return sch;
4685
4685
  }
4686
- _checkUnique(id) {
4687
- if (this.schemas[id] || this.refs[id]) {
4688
- throw new Error(`schema with key or id "${id}" already exists`);
4686
+ _checkUnique(id2) {
4687
+ if (this.schemas[id2] || this.refs[id2]) {
4688
+ throw new Error(`schema with key or id "${id2}" already exists`);
4689
4689
  }
4690
4690
  }
4691
4691
  _compileSchemaEnv(sch) {
@@ -7217,7 +7217,9 @@ var methods = {
7217
7217
  report: "msg.report",
7218
7218
  ask: "msg.ask",
7219
7219
  read: "msg.read",
7220
- leave: "session.leave"
7220
+ leave: "session.leave",
7221
+ task: "dashboard.task",
7222
+ artifact: "dashboard.artifact"
7221
7223
  };
7222
7224
 
7223
7225
  // node_modules/zod/v3/external.js
@@ -13031,38 +13033,38 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
13031
13033
  doc.write(`const newResult = {}`);
13032
13034
  for (const key of normalized.keys) {
13033
13035
  if (normalized.optionalKeys.has(key)) {
13034
- const id = ids[key];
13035
- doc.write(`const ${id} = ${parseStr(key)};`);
13036
+ const id2 = ids[key];
13037
+ doc.write(`const ${id2} = ${parseStr(key)};`);
13036
13038
  const k = esc(key);
13037
13039
  doc.write(`
13038
- if (${id}.issues.length) {
13040
+ if (${id2}.issues.length) {
13039
13041
  if (input[${k}] === undefined) {
13040
13042
  if (${k} in input) {
13041
13043
  newResult[${k}] = undefined;
13042
13044
  }
13043
13045
  } else {
13044
13046
  payload.issues = payload.issues.concat(
13045
- ${id}.issues.map((iss) => ({
13047
+ ${id2}.issues.map((iss) => ({
13046
13048
  ...iss,
13047
13049
  path: iss.path ? [${k}, ...iss.path] : [${k}],
13048
13050
  }))
13049
13051
  );
13050
13052
  }
13051
- } else if (${id}.value === undefined) {
13053
+ } else if (${id2}.value === undefined) {
13052
13054
  if (${k} in input) newResult[${k}] = undefined;
13053
13055
  } else {
13054
- newResult[${k}] = ${id}.value;
13056
+ newResult[${k}] = ${id2}.value;
13055
13057
  }
13056
13058
  `);
13057
13059
  } else {
13058
- const id = ids[key];
13059
- doc.write(`const ${id} = ${parseStr(key)};`);
13060
+ const id2 = ids[key];
13061
+ doc.write(`const ${id2} = ${parseStr(key)};`);
13060
13062
  doc.write(`
13061
- if (${id}.issues.length) payload.issues = payload.issues.concat(${id}.issues.map(iss => ({
13063
+ if (${id2}.issues.length) payload.issues = payload.issues.concat(${id2}.issues.map(iss => ({
13062
13064
  ...iss,
13063
13065
  path: iss.path ? [${esc(key)}, ...iss.path] : [${esc(key)}]
13064
13066
  })));`);
13065
- doc.write(`newResult[${esc(key)}] = ${id}.value`);
13067
+ doc.write(`newResult[${esc(key)}] = ${id2}.value`);
13066
13068
  }
13067
13069
  }
13068
13070
  doc.write(`payload.value = newResult;`);
@@ -14795,13 +14797,13 @@ var JSONSchemaGenerator = class {
14795
14797
  const defsSegment = this.target === "draft-2020-12" ? "$defs" : "definitions";
14796
14798
  if (params.external) {
14797
14799
  const externalId = params.external.registry.get(entry[0])?.id;
14798
- const uriGenerator = params.external.uri ?? ((id2) => id2);
14800
+ const uriGenerator = params.external.uri ?? ((id3) => id3);
14799
14801
  if (externalId) {
14800
14802
  return { ref: uriGenerator(externalId) };
14801
14803
  }
14802
- const id = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`;
14803
- entry[1].defId = id;
14804
- return { defId: id, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id}` };
14804
+ const id2 = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`;
14805
+ entry[1].defId = id2;
14806
+ return { defId: id2, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id2}` };
14805
14807
  }
14806
14808
  if (entry[1] === root) {
14807
14809
  return { ref: "#" };
@@ -14849,8 +14851,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14849
14851
  continue;
14850
14852
  }
14851
14853
  }
14852
- const id = this.metadataRegistry.get(entry[0])?.id;
14853
- if (id) {
14854
+ const id2 = this.metadataRegistry.get(entry[0])?.id;
14855
+ if (id2) {
14854
14856
  extractToDef(entry);
14855
14857
  continue;
14856
14858
  }
@@ -14904,10 +14906,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14904
14906
  console.warn(`Invalid target: ${this.target}`);
14905
14907
  }
14906
14908
  if (params.external?.uri) {
14907
- const id = params.external.registry.get(schema)?.id;
14908
- if (!id)
14909
+ const id2 = params.external.registry.get(schema)?.id;
14910
+ if (!id2)
14909
14911
  throw new Error("Schema is missing an `id` property");
14910
- result.$id = params.external.uri(id);
14912
+ result.$id = params.external.uri(id2);
14911
14913
  }
14912
14914
  Object.assign(result, root.def);
14913
14915
  const defs = params.external?.defs ?? {};
@@ -19940,7 +19942,7 @@ var ExperimentalServerTasks = class {
19940
19942
  if (hasPreviousToolUse) {
19941
19943
  const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
19942
19944
  const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
19943
- if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
19945
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id2) => toolResultIds.has(id2))) {
19944
19946
  throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
19945
19947
  }
19946
19948
  }
@@ -20365,7 +20367,7 @@ var Server = class extends Protocol {
20365
20367
  if (hasPreviousToolUse) {
20366
20368
  const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
20367
20369
  const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
20368
- if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
20370
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id2) => toolResultIds.has(id2))) {
20369
20371
  throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
20370
20372
  }
20371
20373
  }
@@ -21457,8 +21459,70 @@ var StdioServerTransport = class {
21457
21459
  }
21458
21460
  };
21459
21461
 
21462
+ // src/shared/dashboard.ts
21463
+ var DASHBOARD_LIMITS = {
21464
+ recordsPerKind: 200,
21465
+ runsPerTask: 100,
21466
+ htmlBytes: 256 * 1024,
21467
+ htmlBytesPerSquad: 8 * 1024 * 1024
21468
+ };
21469
+
21470
+ // src/shared/dashboard-schemas.ts
21471
+ var id = external_exports.string().min(1).max(128);
21472
+ var text = external_exports.string().max(8e3);
21473
+ var artifacts = external_exports.array(id).max(8);
21474
+ var taskFields = {
21475
+ action: external_exports.enum(["create", "update", "get", "list", "archive", "restore"]).default("list"),
21476
+ id: id.optional(),
21477
+ title: external_exports.string().trim().min(1).max(200).optional(),
21478
+ description: text.optional(),
21479
+ acceptance: text.optional(),
21480
+ position: external_exports.number().finite().optional(),
21481
+ artifact_ids: artifacts.optional(),
21482
+ archived: external_exports.boolean().optional(),
21483
+ offset: external_exports.number().int().min(0).default(0),
21484
+ limit: external_exports.number().int().min(1).max(50).default(20)
21485
+ };
21486
+ var questionFields = {
21487
+ target: external_exports.enum(["commander", "user"]).default("commander"),
21488
+ action: external_exports.enum(["create", "update", "get", "list", "withdraw", "handle"]).default("create"),
21489
+ id: id.optional(),
21490
+ task_id: id.optional(),
21491
+ version: external_exports.number().int().positive().optional(),
21492
+ description: text.optional(),
21493
+ kind: external_exports.enum(["single", "multiple", "text", "confirm"]).optional(),
21494
+ options: external_exports.array(external_exports.object({ id, label: external_exports.string().trim().min(1).max(300) }).strict()).max(20).optional(),
21495
+ artifact_ids: artifacts.optional(),
21496
+ result: text.optional(),
21497
+ status: external_exports.enum(["pending", "answered", "handled", "withdrawn"]).optional(),
21498
+ offset: external_exports.number().int().min(0).default(0),
21499
+ limit: external_exports.number().int().min(1).max(50).default(20)
21500
+ };
21501
+ var artifactFields = {
21502
+ action: external_exports.enum(["publish", "get", "list"]).default("publish"),
21503
+ id: id.optional(),
21504
+ version: external_exports.number().int().positive().optional(),
21505
+ title: external_exports.string().trim().min(1).max(200).optional(),
21506
+ html: external_exports.string().min(1).refine((s) => Buffer.byteLength(s) <= DASHBOARD_LIMITS.htmlBytes, "MESSAGE_TOO_LARGE").optional(),
21507
+ offset: external_exports.number().int().min(0).default(0),
21508
+ limit: external_exports.number().int().min(1).max(50).default(20)
21509
+ };
21510
+ var answerSchema = external_exports.object({
21511
+ question_id: id,
21512
+ version: external_exports.number().int().positive(),
21513
+ submission_id: external_exports.string().uuid(),
21514
+ selected: external_exports.array(id).max(20).default([]),
21515
+ text: text.default(""),
21516
+ confirmed: external_exports.boolean().optional()
21517
+ }).strict();
21518
+ var userMessageSchema = external_exports.object({
21519
+ squad_id: id,
21520
+ submission_id: external_exports.string().uuid(),
21521
+ text: external_exports.string().trim().min(1).max(8e3)
21522
+ }).strict();
21523
+
21460
21524
  // src/shared/schemas.ts
21461
- var text = external_exports.string().min(1).refine((v) => Buffer.byteLength(v) <= LIMITS.maxBody, "MESSAGE_TOO_LARGE");
21525
+ var text2 = external_exports.string().min(1).refine((v) => Buffer.byteLength(v) <= LIMITS.maxBody, "MESSAGE_TOO_LARGE");
21462
21526
  var data = external_exports.record(external_exports.unknown()).refine((v) => Buffer.byteLength(JSON.stringify(v)) <= LIMITS.maxData, "MESSAGE_TOO_LARGE").optional();
21463
21527
  var wait = external_exports.number().min(0).max(300).default(0);
21464
21528
  var identity = { _cmdr_session: external_exports.string().min(1).max(256).optional() };
@@ -21468,7 +21532,7 @@ var schemas = {
21468
21532
  role: external_exports.enum(["commander", "executor"]).optional(),
21469
21533
  squad: external_exports.string().optional(),
21470
21534
  name: external_exports.string().trim().min(1).max(64).optional(),
21471
- note: text.optional(),
21535
+ note: text2.optional(),
21472
21536
  squad_name: external_exports.string().trim().min(1).max(64).optional(),
21473
21537
  takeover: external_exports.boolean().default(false),
21474
21538
  standby: external_exports.enum(["auto", "manual"]).optional(),
@@ -21483,17 +21547,27 @@ var schemas = {
21483
21547
  report: external_exports.object({
21484
21548
  ...identity,
21485
21549
  status: external_exports.enum(["ready", "working", "blocked", "done", "failed", "cancelled"]),
21486
- message: text,
21550
+ message: text2,
21487
21551
  reply_to: external_exports.string().optional(),
21488
21552
  data
21489
21553
  }).strict(),
21490
- ask: external_exports.object({ ...identity, question: text, wait, reply_to: external_exports.string().optional(), data }).strict(),
21554
+ ask: external_exports.object({
21555
+ ...identity,
21556
+ ...questionFields,
21557
+ question: text2.optional(),
21558
+ wait,
21559
+ reply_to: external_exports.string().optional(),
21560
+ data
21561
+ }).strict(),
21562
+ task: external_exports.object({ ...identity, ...taskFields }).strict(),
21563
+ artifact: external_exports.object({ ...identity, ...artifactFields }).strict(),
21491
21564
  send: external_exports.object({
21492
21565
  ...identity,
21493
21566
  to: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1).max(1e3)]),
21494
- message: text,
21567
+ message: text2,
21495
21568
  type: external_exports.enum(["command", "cancel", "answer", "info"]).default("command"),
21496
21569
  task_key: external_exports.string().min(1).max(128).optional(),
21570
+ task_id: external_exports.string().min(1).max(128).optional(),
21497
21571
  reassign: external_exports.string().optional(),
21498
21572
  attention: external_exports.boolean().optional(),
21499
21573
  priority: external_exports.enum(["high", "normal", "low"]).optional(),
@@ -21511,11 +21585,11 @@ var schemas = {
21511
21585
  recover: external_exports.boolean().default(false),
21512
21586
  full: external_exports.boolean().default(false)
21513
21587
  }).strict(),
21514
- leave: external_exports.object({ ...identity, dissolve: external_exports.boolean().default(false), message: text.optional() }).strict()
21588
+ leave: external_exports.object({ ...identity, dissolve: external_exports.boolean().default(false), message: text2.optional() }).strict()
21515
21589
  };
21516
21590
 
21517
21591
  // src/shared/version.ts
21518
- var VERSION = true ? "0.4.0" : MIN_CLIENT_VERSION;
21592
+ var VERSION = true ? "0.6.0" : MIN_CLIENT_VERSION;
21519
21593
  var PROTOCOL = 1;
21520
21594
  function newer(a, b) {
21521
21595
  const x = a.split(".").map(Number), y = b.split(".").map(Number);
@@ -21529,6 +21603,7 @@ function newer(a, b) {
21529
21603
  function detectAgent(env = process.env, hook) {
21530
21604
  if (env.CMDR_AGENT && /^[a-z][a-z0-9_-]{0,63}$/.test(env.CMDR_AGENT)) return env.CMDR_AGENT;
21531
21605
  if (env.ZCODE_PLUGIN_ROOT || env.ZCODE_PLUGIN_ID) return "zcode";
21606
+ if (env.KIMI_PLUGIN_ROOT) return "kimi";
21532
21607
  if (env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_CODE_ENTRYPOINT) return "claude";
21533
21608
  if (hook?.transcript_path && String(hook.transcript_path).includes("/.claude/")) return "claude";
21534
21609
  if (env.CODEX_HOME || env.CODEX_THREAD_ID || env.CODEX_SESSION_ID || hook?.turn_id)
@@ -21736,26 +21811,26 @@ var Rpc = class extends EventEmitter {
21736
21811
  if (this.socket.destroyed) return Promise.reject(new CmdrError("DAEMON_UNAVAILABLE"));
21737
21812
  if (signal?.aborted) return Promise.reject(new CmdrError("REQUEST_CANCELLED"));
21738
21813
  return new Promise((resolve2, reject) => {
21739
- const id = this.next++;
21814
+ const id2 = this.next++;
21740
21815
  const cancel = (code) => {
21741
- const p = this.pending.get(id);
21816
+ const p = this.pending.get(id2);
21742
21817
  if (!p) return;
21743
- this.pending.delete(id);
21818
+ this.pending.delete(id2);
21744
21819
  clearTimeout(p.timer);
21745
21820
  p.cleanup();
21746
- this.notify("rpc.cancel", { id });
21821
+ this.notify("rpc.cancel", { id: id2 });
21747
21822
  reject(new CmdrError(code));
21748
21823
  };
21749
21824
  const abort = () => cancel("REQUEST_CANCELLED");
21750
21825
  const timer = setTimeout(() => cancel("DAEMON_UNAVAILABLE"), timeout);
21751
- this.pending.set(id, {
21826
+ this.pending.set(id2, {
21752
21827
  resolve: resolve2,
21753
21828
  reject,
21754
21829
  timer,
21755
21830
  cleanup: () => signal?.removeEventListener("abort", abort)
21756
21831
  });
21757
21832
  signal?.addEventListener("abort", abort, { once: true });
21758
- this.send({ jsonrpc: "2.0", id, method, params });
21833
+ this.send({ jsonrpc: "2.0", id: id2, method, params });
21759
21834
  });
21760
21835
  }
21761
21836
  notify(method, params) {
@@ -22047,12 +22122,14 @@ function closeClients() {
22047
22122
  }
22048
22123
  var server = new McpServer({ name: "cmdr", version: VERSION });
22049
22124
  var descriptions = {
22050
- join: "Create/join a squad. For /cmdr <name>, pass squad_name for atomic find-or-create; role defaults to executor. Explicitly claim role=commander, use takeover=true for handover or rebind=member_id for a new endpoint. Otherwise specify role and optional squad ID/name. Use standby=auto; Claude/ZCode must run listener.arm.command with the indicated native host tool, and re-arm on termination. Codex is daemon driven. Inspect listener health before ending the turn. Executors report ready after joining. Reply with user_reply and follow listener health; bounded read(wait=me.recommended_wait) is the manual fallback.",
22125
+ join: "Create/join a squad. For /cmdr <name>, pass squad_name for atomic find-or-create; role defaults to executor. Explicitly claim role=commander, use takeover=true for handover or rebind=member_id for a new endpoint. Otherwise specify role and optional squad ID/name. Use standby=auto; Claude/ZCode/Kimi must run listener.arm.command with the indicated native host tool, and re-arm on termination. Codex is daemon driven. Inspect listener health before ending the turn. Executors report ready after joining. Reply with user_reply and follow listener health; bounded read(wait=me.recommended_wait) is the manual fallback.",
22051
22126
  list: "Show task ownership, unacked_for, execution activity, connection presence and listener health. Offline never authorizes reassignment. Use full=true for expanded session metadata; listings never include command bodies. Read your own messages with read(id=...). scope=all lists squads and sessions.",
22052
- send: "Commander: dispatch clear tasks with acceptance criteria, or answer an ask using type=answer and reply_to. Use task_key to prevent duplicate tickets and reassign=<command id> for a gated handover preserving the original task_key. type=cancel with reply_to requests a safe stop. to accepts all, member_id, sid, unique sid prefix or member name.",
22127
+ send: "Commander: dispatch clear tasks with acceptance criteria, optionally linked to task_id from the dashboard task tool, or answer an executor ask using type=answer and reply_to. Use task_key to prevent duplicate tickets and reassign=<command id> for a gated handover preserving the original task/task_key. type=cancel with reply_to requests a safe stop. to accepts all, member_id, sid, unique sid prefix or member name.",
22053
22128
  report: "Executor: report ready (cwd/capabilities), working, blocked, done, failed or cancelled; include reply_to for the command.",
22054
- ask: "Executor: ask the commander for guidance. Optional wait waits for the matching answer; use me.recommended_wait as the upper bound.",
22055
- read: "Fetch messages in priority order (reading dequeues). Use wait=me.recommended_wait to stand by, peek to inspect or history to review delivered messages. Use recover=true for all unfinished commands (non-consuming), id for a non-consuming message lookup (blocked replacements return REASSIGNMENT_PENDING), full for squad details. With a healthy listener, end the turn. Claude/ZCode must first arm the built-in host watcher from listener.arm. Only when host wake is unavailable, do at most two waits and explain manual continuation.",
22129
+ ask: "Executor: ask the commander (default target=commander). Commander: target=user creates a durable dashboard question (kind=single|multiple|text|confirm); use action=list|get|update|withdraw|handle, id and current version. handle requires result. User replies arrive in read; query status=answered to recover unhandled decisions. User questions do not use wait.",
22130
+ task: "Maintain dashboard tasks. Commander uses action=create|update|archive|restore; title is required for create. Members can list/get within their squad. Dispatch with send(task_id=...). Execution states follow commands/reports; unfinished work cannot be archived. list is paginated; get includes durable execution history.",
22131
+ artifact: "Commander publishes self-contained HTML as an isolated dashboard explanation (256 KiB maximum). action=publish requires title and html; updating also requires id and current version. Link returned id via task/ask artifact_ids. Members can list/get. User answers always use built-in forms outside HTML. Updating an artifact increments versions of pending questions that reference it.",
22132
+ read: "Fetch messages in priority order (reading dequeues). Use wait=me.recommended_wait to stand by, peek to inspect or history to review delivered messages. Use recover=true for all unfinished commands (non-consuming), id for a non-consuming message lookup (blocked replacements return REASSIGNMENT_PENDING), full for squad details. With a healthy listener, end the turn. Claude/ZCode/Kimi must first arm the built-in host watcher from listener.arm. Only when host wake is unavailable, do at most two waits and explain manual continuation.",
22056
22133
  leave: "Leave the squad. Commander departure orphans it; dissolve=true disbands it. Messages already queued remain readable."
22057
22134
  };
22058
22135
  for (const name of Object.keys(schemas)) {
@@ -22062,6 +22139,11 @@ for (const name of Object.keys(schemas)) {
22062
22139
  async (args, extra) => {
22063
22140
  try {
22064
22141
  const { _cmdr_session, ...params } = args;
22142
+ if (agent === "kimi" && !_cmdr_session && !process.env.CMDR_SESSION_ID)
22143
+ throw new CmdrError(
22144
+ "SESSION_STAMP_REQUIRED",
22145
+ 'Kimi Code shares one cmdr MCP process per workspace, so every cmdr call must carry this session identity: retry with _cmdr_session="<session-id>". The id is the session_... value the cmdr identity skill renders for this session (${KIMI_SESSION_ID}); the main agent passes it on every call, including calls made on behalf of subagents.'
22146
+ );
22065
22147
  const selected = await forSession(_cmdr_session);
22066
22148
  const result = await selected.call(methods[name], params, extra.signal);
22067
22149
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: cmdr-identity
3
+ description: Kimi Code session identity for cmdr; carry it on every cmdr MCP call so the shared per-workspace MCP process routes to the right member.
4
+ ---
5
+
6
+ You run as a Kimi Code session inside a cmdr squad. Kimi Code shares ONE cmdr MCP process per workspace, so the daemon cannot tell which session is calling unless each call identifies itself.
7
+
8
+ Your session id for this conversation: ${KIMI_SESSION_ID}
9
+
10
+ - Pass `_cmdr_session` with exactly that value on EVERY cmdr MCP tool call (`join`, `list`, `send`, `report`, `ask`, `read`, `leave`, `task`, `artifact`), including calls you make on behalf of subagents — subagents never receive this injection.
11
+ - If a cmdr call is rejected for a missing or mismatched `_cmdr_session`, retry it with the id given in the error message; that id is always this session's current id (resuming a conversation never changes it).
12
+ - Use the value only for `_cmdr_session`; it identifies this session to the local cmdr daemon and is not a credential for anything else.
@@ -5,7 +5,7 @@ metadata:
5
5
  source: https://github.com/njugray/cmdr
6
6
  ---
7
7
 
8
- Connect existing sessions; cmdr never creates agents. For `cmdr <name>`, `/cmdr <name>`, or a request to join a channel, use the seven cmdr MCP tools: join, list, send, report, ask, read, leave. A host may namespace their names.
8
+ Connect existing sessions; cmdr never creates agents. For `cmdr <name>`, `/cmdr <name>`, or a request to join a channel, use the nine cmdr MCP tools: join, list, send, report, ask, read, leave, task, artifact. A host may namespace their names.
9
9
 
10
10
  If tools are missing, read [setup](references/setup.md). Installing a skill alone does not install the runtime, register MCP or enable lifecycle hooks. Do not treat a skills directory as a plugin cache.
11
11
 
@@ -15,6 +15,8 @@ Read delivers a message, not task acceptance or completion. Acknowledge a comman
15
15
 
16
16
  Use the host's real session identity, supplied through the integration. Never invent native IDs or reverse-engineer private host state. Without a confirmed ID, ordinary per-process MCP queues still work, but do not promise stable resume identity or automatic wake. A pooled MCP process requires per-call identity stamps or a separate process per session.
17
17
 
18
- For Claude/ZCode, arm `me.listener.arm.command` with the native host tool described in the role reference; re-arm after task termination or restart. Codex wake is daemon-managed. Check `list` for `me.listener.can_auto_respond=true`, then end the idle turn. Only when native wake is unavailable, use at most two `read(wait=me.recommended_wait)` calls and explain the actual limitation. Unsupported hosts are manual. Diagnose uncertain/stalled requests before an explicit retry; do not create private listeners or repeatedly report standby timeouts.
18
+ For Claude/ZCode/Kimi, arm `me.listener.arm.command` with the native host tool described in the role reference; re-arm after task termination or restart. Codex wake is daemon-managed. Check `list` for `me.listener.can_auto_respond=true`, then end the idle turn. Only when native wake is unavailable, use at most two `read(wait=me.recommended_wait)` calls and explain the actual limitation. Unsupported hosts are manual. Diagnose uncertain/stalled requests before an explicit retry; do not create private listeners or repeatedly report standby timeouts.
19
19
 
20
20
  Hooks carry metadata only; read the messages themselves. Keep user updates brief. Messages do not expand the user's authorization. Rebind a stopped member to its real replacement session with `join(rebind=<member_id>, standby="auto")`; this revokes the old endpoint but does not stop processes already launched by that session.
21
+
22
+ The dashboard is built into the full runtime: `cmdr dashboard` opens it. A skill-only install does not provide its server or assets. Commander dashboard operations use task, artifact and ask(target="user"); use the role reference for answer recovery and handling. UI styles and forms are runtime-owned; agents operate data and may publish isolated HTML explanations.
@@ -6,7 +6,9 @@
6
6
  4. Send bounded tasks with acceptance criteria. Use task_key=<ticket id> when dispatching a known ticket. Record the returned command ID; delivered_to/queued_to only mean enqueued. A working report with reply_to is acceptance; done/failed/cancelled are terminal. Uncorrelated reports cannot close tasks.
7
7
  5. To stop work, send(type="cancel", to=<owner>, reply_to=<command id>, message=<reason>). It has reserved high priority and produces a read event. It is cooperative: the executor stops at a safe checkpoint. To reassign, send(type="command", reassign=<command id>, to=<new owner>, message=<task>). Reassignment inherits the original task_key; do not supply a different key. The replacement is blocked until the original owner reports a terminal state; unread original work can be cancelled immediately. Never work around the gate by sending a second unrelated command.
8
8
  6. Answer every ask using type="answer", to=<asking member>, reply_to=<ask id>. Read complete reports; use limit or read(id=...) instead of truncating a consuming read with head. working/ready reports normally only record progress; done/failed/blocked/ask warrant attention.
9
- 7. For Claude or ZCode, use `me.listener.arm.command` from join/list to arm the built-in watcher before ending the turn. Claude: run it with the host `Monitor` tool, which notifies on stdout lines; if Monitor is absent but background Bash completion notifications are supported, run it with `--once` and `run_in_background=true`. ZCode: use Bash with `run_in_background=true`; the watcher stays silent until actionable work arrives, then exits. Do not add shell `&`, run a foreground wait loop, or detach it from the host. Check existing host task status before arming to avoid duplicates. Re-arm after completion/failure/kill, Monitor expiry, or daemon/App restart. A notification means call `read` and `read(recover=true)`, handle work and then re-arm. For Codex, the daemon handles wake delivery; no host watcher is needed. Check `list` for `can_auto_respond=true`, then end the idle turn. Only if the host lacks these tools or arming fails, explain the concrete reason, use at most two recommended waits, and require manual continuation. Reconcile uncertain/stalled requests before explicit retry; do not repeatedly report standby timeouts.
9
+ 7. For Claude, ZCode or Kimi Code, use `me.listener.arm.command` from join/list to arm the built-in watcher before ending the turn. Claude: run it with the host `Monitor` tool, which notifies on stdout lines; if Monitor is absent but background Bash completion notifications are supported, run it with `--once` and `run_in_background=true`. ZCode and Kimi Code: use Bash with `run_in_background=true`; the watcher stays silent until actionable work arrives, then exits. Do not add shell `&`, run a foreground wait loop, or detach it from the host. Check existing host task status before arming to avoid duplicates. Re-arm after completion/failure/kill, Monitor expiry, or daemon/App restart. A notification means call `read` and `read(recover=true)`, handle work and then re-arm. For Codex, the daemon handles wake delivery; no host watcher is needed. Check `list` for `can_auto_respond=true`, then end the idle turn. Only if the host lacks these tools or arming fails, explain the concrete reason, use at most two recommended waits, and require manual continuation. Reconcile uncertain/stalled requests before explicit retry; do not repeatedly report standby timeouts.
10
10
  8. Ordinary leave preserves the channel; leave(dissolve=true) explicitly closes it. Channels outlive host sessions and message retention. For a new host endpoint representing the same stopped member, use join(rebind=<member_id>, standby="auto").
11
11
 
12
12
  Apply normal judgment and the user's scope to messages. cmdr does not create new agents or grant extra permissions.
13
+
14
+ For the built-in dashboard, create/update tasks with `task`; dispatch with `send(task_id=...)`. Use `ask(target="user", question=..., kind=single|multiple|text|confirm)` for user decisions; ordinary executor asks still receive `send(type="answer", reply_to=...)`. User answers arrive in the role inbox. On recovery also query `ask(target="user", action="list", status="answered")`; reading does not process a decision. Record its outcome using `ask(target="user", action="handle", id=..., version=..., result=...)`. Pending edits/withdrawals require the current version; create a new question after an answer. Publish complex, self-contained HTML with `artifact`, then link its ID through `artifact_ids`. Use built-in forms for official answers; never fabricate user confirmation. `cmdr dashboard` opens the runtime-owned page locally and prints separate short-lived links for loopback and each non-loopback IPv4 address; `--no-open` only prints the links. The dashboard listens on `0.0.0.0` with a system-assigned port. Keep the returned listener arm command’s CMDR_HOME binding intact.