kimi-plugin-cc 2.0.7 → 2.2.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 (85) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +50 -0
  4. package/README.md +22 -6
  5. package/agents/kimi-ask.md +3 -0
  6. package/agents/kimi-challenge.md +3 -1
  7. package/agents/kimi-rescue.md +3 -0
  8. package/agents/kimi-review.md +3 -1
  9. package/commands/ask.md +6 -2
  10. package/commands/challenge.md +6 -2
  11. package/commands/rescue.md +6 -2
  12. package/commands/review.md +6 -2
  13. package/dist/cli-client.js +36 -13
  14. package/dist/commands/ask.js +16 -7
  15. package/dist/commands/cli-helpers.js +80 -0
  16. package/dist/commands/pursue.js +4 -2
  17. package/dist/commands/repair-sessions.js +2 -1
  18. package/dist/commands/replay.js +3 -2
  19. package/dist/commands/rescue.js +19 -9
  20. package/dist/commands/result.js +7 -1
  21. package/dist/commands/review-gate.js +2 -1
  22. package/dist/commands/review.js +8 -5
  23. package/dist/commands/setup.js +2 -1
  24. package/dist/commands/status.js +2 -2
  25. package/dist/commands/swarm.js +7 -3
  26. package/dist/companion.js +6 -0
  27. package/dist/help.js +40 -0
  28. package/dist/hooks/config-safety.js +18 -9
  29. package/dist/job-store.js +43 -6
  30. package/dist/jobs.js +7 -3
  31. package/dist/kimi-engine.js +1 -0
  32. package/dist/kimi-errors.js +35 -1
  33. package/dist/kimi-timeouts.js +7 -7
  34. package/dist/parsing.js +87 -13
  35. package/dist/render.js +19 -1
  36. package/dist/session-recovery.js +203 -0
  37. package/dist/vendor/smol-toml/date.js +50 -21
  38. package/dist/vendor/smol-toml/error.js +6 -2
  39. package/dist/vendor/smol-toml/extract.js +259 -66
  40. package/dist/vendor/smol-toml/parse.js +64 -48
  41. package/dist/vendor/smol-toml/primitive.js +107 -135
  42. package/dist/vendor/smol-toml/struct.js +106 -134
  43. package/dist/vendor/smol-toml/util.js +22 -78
  44. package/dist/version.js +1 -1
  45. package/docs/migration.md +3 -3
  46. package/docs/registry-releases.md +35 -1
  47. package/docs/safety.md +49 -2
  48. package/package.json +2 -2
  49. package/plugins/kimi-codex/.codex-plugin/plugin.json +1 -1
  50. package/plugins/kimi-codex/dist/cli-client.js +36 -13
  51. package/plugins/kimi-codex/dist/commands/ask.js +16 -7
  52. package/plugins/kimi-codex/dist/commands/cli-helpers.js +80 -0
  53. package/plugins/kimi-codex/dist/commands/pursue.js +4 -2
  54. package/plugins/kimi-codex/dist/commands/repair-sessions.js +2 -1
  55. package/plugins/kimi-codex/dist/commands/replay.js +3 -2
  56. package/plugins/kimi-codex/dist/commands/rescue.js +19 -9
  57. package/plugins/kimi-codex/dist/commands/result.js +7 -1
  58. package/plugins/kimi-codex/dist/commands/review-gate.js +2 -1
  59. package/plugins/kimi-codex/dist/commands/review.js +8 -5
  60. package/plugins/kimi-codex/dist/commands/setup.js +2 -1
  61. package/plugins/kimi-codex/dist/commands/status.js +2 -2
  62. package/plugins/kimi-codex/dist/commands/swarm.js +7 -3
  63. package/plugins/kimi-codex/dist/companion.js +6 -0
  64. package/plugins/kimi-codex/dist/help.js +40 -0
  65. package/plugins/kimi-codex/dist/hooks/config-safety.js +18 -9
  66. package/plugins/kimi-codex/dist/job-store.js +43 -6
  67. package/plugins/kimi-codex/dist/jobs.js +7 -3
  68. package/plugins/kimi-codex/dist/kimi-engine.js +1 -0
  69. package/plugins/kimi-codex/dist/kimi-errors.js +35 -1
  70. package/plugins/kimi-codex/dist/kimi-timeouts.js +7 -7
  71. package/plugins/kimi-codex/dist/parsing.js +87 -13
  72. package/plugins/kimi-codex/dist/render.js +19 -1
  73. package/plugins/kimi-codex/dist/session-recovery.js +203 -0
  74. package/plugins/kimi-codex/dist/vendor/smol-toml/date.js +50 -21
  75. package/plugins/kimi-codex/dist/vendor/smol-toml/error.js +6 -2
  76. package/plugins/kimi-codex/dist/vendor/smol-toml/extract.js +259 -66
  77. package/plugins/kimi-codex/dist/vendor/smol-toml/parse.js +64 -48
  78. package/plugins/kimi-codex/dist/vendor/smol-toml/primitive.js +107 -135
  79. package/plugins/kimi-codex/dist/vendor/smol-toml/struct.js +106 -134
  80. package/plugins/kimi-codex/dist/vendor/smol-toml/util.js +22 -78
  81. package/plugins/kimi-codex/dist/version.js +1 -1
  82. package/plugins/kimi-codex/skills/kimi-ask/SKILL.md +3 -1
  83. package/plugins/kimi-codex/skills/kimi-challenge/SKILL.md +2 -1
  84. package/plugins/kimi-codex/skills/kimi-rescue/SKILL.md +3 -1
  85. package/plugins/kimi-codex/skills/kimi-review/SKILL.md +3 -2
@@ -27,158 +27,130 @@
27
27
  */
28
28
  import { parseString } from './primitive.js';
29
29
  import { extractValue } from './extract.js';
30
- import { getStringEnd, indexOfNewline, skipComment, skipVoid } from './util.js';
30
+ import { skipVoid } from './util.js';
31
31
  import { TomlError } from './error.js';
32
- let KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
33
- export function parseKey(str, ptr, end = '=') {
34
- let dot = ptr - 1;
32
+ /** @internal */
33
+ export function parseKey(ctx, end = 0x3d /* = */) {
34
+ // States:
35
+ // 0: before first char
36
+ // 1: parsing bare key
37
+ // 2: after key component
38
+ let startPtr;
39
+ let state = 0;
35
40
  let parsed = [];
36
- let endPtr = str.indexOf(end, ptr);
37
- if (endPtr < 0) {
38
- throw new TomlError('incomplete key-value: cannot find end of key', {
39
- toml: str,
40
- ptr: ptr,
41
- });
42
- }
41
+ let sliceStart;
42
+ let c = ctx.s.charCodeAt(startPtr = ctx.p);
43
43
  do {
44
- let c = str[ptr = ++dot];
45
- // If it's whitespace, ignore
46
- if (c !== ' ' && c !== '\t') {
47
- // If it's a string
48
- if (c === '"' || c === '\'') {
49
- if (c === str[ptr + 1] && c === str[ptr + 2]) {
50
- throw new TomlError('multiline strings are not allowed in keys', {
51
- toml: str,
52
- ptr: ptr,
53
- });
54
- }
55
- let eos = getStringEnd(str, ptr);
56
- if (eos < 0) {
57
- throw new TomlError('unfinished string encountered', {
58
- toml: str,
59
- ptr: ptr,
60
- });
61
- }
62
- dot = str.indexOf('.', eos);
63
- let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
64
- let newLine = indexOfNewline(strEnd);
65
- if (newLine > -1) {
66
- throw new TomlError('newlines are not allowed in keys', {
67
- toml: str,
68
- ptr: ptr + dot + newLine,
69
- });
70
- }
71
- if (strEnd.trimStart()) {
72
- throw new TomlError('found extra tokens after the string part', {
73
- toml: str,
74
- ptr: eos,
75
- });
76
- }
77
- if (endPtr < eos) {
78
- endPtr = str.indexOf(end, eos);
79
- if (endPtr < 0) {
80
- throw new TomlError('incomplete key-value: cannot find end of key', {
81
- toml: str,
82
- ptr: ptr,
83
- });
84
- }
85
- }
86
- parsed.push(parseString(str, ptr, eos));
87
- }
88
- else {
89
- // Normal raw key part consumption and validation
90
- dot = str.indexOf('.', ptr);
91
- let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
92
- if (!KEY_PART_RE.test(part)) {
93
- throw new TomlError('only letter, numbers, dashes and underscores are allowed in keys', {
94
- toml: str,
95
- ptr: ptr,
96
- });
97
- }
98
- parsed.push(part.trimEnd());
44
+ // End of key
45
+ if (c === end) {
46
+ if (!state)
47
+ TomlError.x('unexpected end of key', ctx);
48
+ if (state === 1)
49
+ parsed.push(ctx.s.slice(sliceStart, ctx.p));
50
+ return ctx.p++, parsed;
51
+ }
52
+ // Dotted key separator
53
+ else if (c === 0x2e /* . */) {
54
+ if (!state)
55
+ TomlError.x('illegal empty bare key', ctx);
56
+ if (state === 1)
57
+ parsed.push(ctx.s.slice(sliceStart, ctx.p));
58
+ state = 0;
59
+ }
60
+ // Quoted key
61
+ else if (!state && (c === 0x22 /* " */ || c === 0x27 /* ' */)) {
62
+ if (c === ctx.s.charCodeAt(ctx.p + 1) && c === ctx.s.charCodeAt(ctx.p + 2))
63
+ TomlError.x('illegal quoted key: multiline strings are not allowed', ctx);
64
+ parsed.push(parseString(ctx));
65
+ state = 2;
66
+ ctx.p--;
67
+ }
68
+ // Whitespace; no-op in state 0 and 2, end of bare key in state 1
69
+ else if (c === 0x20 || c === 0x9 /* \t */) {
70
+ if (state === 1) {
71
+ parsed.push(ctx.s.slice(sliceStart, ctx.p));
72
+ state = 2;
99
73
  }
100
74
  }
101
- // Until there's no more dot
102
- } while (dot + 1 && dot < endPtr);
103
- return [parsed, skipVoid(str, endPtr + 1, true, true)];
75
+ // If state is post-key, no character is allowed; otherwise ensure it's a bare-key component
76
+ else if (state === 2 || (c < 0x30 && c !== 0x2d /* - */) || (c > 0x39 && c < 0x41) || (c > 0x5a && c < 0x61 && c !== 0x5f /* _ */) || c > 0x7a) {
77
+ TomlError.x('illegal character in key', ctx);
78
+ }
79
+ // Illegal character
80
+ else if (!state) {
81
+ state = 1;
82
+ sliceStart = ctx.p;
83
+ }
84
+ } while (c = ctx.s.charCodeAt(++ctx.p));
85
+ TomlError.x('incomplete key-value: cannot find end of key', ctx, startPtr);
104
86
  }
105
- export function parseInlineTable(str, ptr, depth, integersAsBigInt) {
106
- let res = {};
87
+ /** @internal */
88
+ export function parseInlineTable(ctx) {
89
+ let startPtr = ctx.p++;
90
+ let res = Object.create(null);
107
91
  let seen = new Set();
108
92
  let c;
109
- ptr++;
110
- while ((c = str[ptr++]) !== '}' && c) {
111
- if (c === ',') {
112
- throw new TomlError('expected value, found comma', {
113
- toml: str,
114
- ptr: ptr - 1,
115
- });
93
+ while (ctx.p < ctx.s.length) {
94
+ skipVoid(ctx);
95
+ if ((c = ctx.s.charCodeAt(ctx.p)) === 0x7d /* } */) {
96
+ ctx.p++;
97
+ return res;
116
98
  }
117
- else if (c === '#')
118
- ptr = skipComment(str, ptr);
119
- else if (c !== ' ' && c !== '\t' && c !== '\n' && c !== '\r') {
120
- let k;
121
- let t = res;
122
- let hasOwn = false;
123
- let [key, keyEndPtr] = parseKey(str, ptr - 1);
124
- for (let i = 0; i < key.length; i++) {
125
- if (i)
126
- t = hasOwn ? t[k] : (t[k] = {});
127
- k = key[i];
128
- if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== 'object' || seen.has(t[k]))) {
129
- throw new TomlError('trying to redefine an already defined value', {
130
- toml: str,
131
- ptr: ptr,
132
- });
133
- }
134
- if (!hasOwn && k === '__proto__') {
135
- Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
136
- }
99
+ let k;
100
+ let t = res;
101
+ let hasOwn = false;
102
+ let errPtr = ctx.p;
103
+ let key = parseKey(ctx);
104
+ for (let i = 0; i < key.length; i++) {
105
+ if (i)
106
+ t = hasOwn ? t[k] : (t[k] = Object.create(null));
107
+ k = key[i];
108
+ if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== 'object' || seen.has(t[k]))) {
109
+ TomlError.x('trying to redefine an already defined value', ctx, errPtr);
110
+ }
111
+ let unsafe = k === '__proto__';
112
+ if (ctx.uk && (unsafe || k === 'constructor')) {
113
+ t = ctx.uk !== 1 && TomlError.x('document contains an unsafe property', ctx, errPtr);
114
+ break;
137
115
  }
138
- if (hasOwn) {
139
- throw new TomlError('trying to redefine an already defined value', {
140
- toml: str,
141
- ptr: ptr,
142
- });
116
+ if (!hasOwn && unsafe) {
117
+ Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
143
118
  }
144
- let [value, valueEndPtr] = extractValue(str, keyEndPtr, '}', depth - 1, integersAsBigInt);
119
+ }
120
+ if (hasOwn) {
121
+ TomlError.x('trying to redefine an already defined value', ctx, errPtr);
122
+ }
123
+ skipVoid(ctx, true, true);
124
+ let value = extractValue(ctx, 0x7d /* } */);
125
+ if (t && typeof (t[k] = value) === 'object')
145
126
  seen.add(value);
146
- t[k] = value;
147
- ptr = valueEndPtr;
127
+ skipVoid(ctx);
128
+ if ((c = ctx.s.charCodeAt(ctx.p++)) === 0x7d /* } */) {
129
+ return res;
148
130
  }
131
+ if (c !== 0x2c /* , */)
132
+ TomlError.x('expected comma or end of structure', ctx, ctx.p - 1);
149
133
  }
150
- if (!c) {
151
- throw new TomlError('unfinished table encountered', {
152
- toml: str,
153
- ptr: ptr,
154
- });
155
- }
156
- return [res, ptr];
134
+ TomlError.x('unfinished table', ctx, startPtr);
157
135
  }
158
- export function parseArray(str, ptr, depth, integersAsBigInt) {
136
+ /** @internal */
137
+ export function parseArray(ctx) {
138
+ let startPtr = ctx.p++;
159
139
  let res = [];
160
140
  let c;
161
- ptr++;
162
- while ((c = str[ptr++]) !== ']' && c) {
163
- if (c === ',') {
164
- throw new TomlError('expected value, found comma', {
165
- toml: str,
166
- ptr: ptr - 1,
167
- });
141
+ while (ctx.p < ctx.s.length) {
142
+ skipVoid(ctx);
143
+ if ((c = ctx.s.charCodeAt(ctx.p)) === 0x5d /* ] */) {
144
+ ctx.p++;
145
+ return res;
168
146
  }
169
- else if (c === '#')
170
- ptr = skipComment(str, ptr);
171
- else if (c !== ' ' && c !== '\t' && c !== '\n' && c !== '\r') {
172
- let e = extractValue(str, ptr - 1, ']', depth - 1, integersAsBigInt);
173
- res.push(e[0]);
174
- ptr = e[1];
147
+ res.push(extractValue(ctx, 0x5d /* ] */));
148
+ skipVoid(ctx);
149
+ if ((c = ctx.s.charCodeAt(ctx.p++)) === 0x5d /* ] */) {
150
+ return res;
175
151
  }
152
+ if (c !== 0x2c /* , */)
153
+ TomlError.x('expected comma or end of structure', ctx, ctx.p - 1);
176
154
  }
177
- if (!c) {
178
- throw new TomlError('unfinished array encountered', {
179
- toml: str,
180
- ptr: ptr,
181
- });
182
- }
183
- return [res, ptr];
155
+ TomlError.x('unfinished array', ctx, startPtr);
184
156
  }
@@ -26,88 +26,32 @@
26
26
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
  import { TomlError } from './error.js';
29
- function isEscaped(str, ptr) {
30
- let i = 0;
31
- while (str[ptr - ++i] === '\\')
32
- ;
33
- return --i && (i % 2);
34
- }
35
- export function indexOfNewline(str, start = 0, end = str.length) {
36
- let idx = str.indexOf('\n', start);
37
- if (str[idx - 1] === '\r')
38
- idx--;
39
- return idx <= end ? idx : -1;
40
- }
41
- export function skipComment(str, ptr) {
42
- for (let i = ptr; i < str.length; i++) {
43
- let c = str[i];
44
- if (c === '\n')
45
- return i;
46
- if (c === '\r' && str[i + 1] === '\n')
47
- return i + 1;
48
- if ((c < '\x20' && c !== '\t') || c === '\x7f') {
49
- throw new TomlError('control characters are not allowed in comments', {
50
- toml: str,
51
- ptr: ptr,
52
- });
53
- }
54
- }
55
- return str.length;
56
- }
57
- export function skipVoid(str, ptr, banNewLines, banComments) {
58
- let c;
59
- while (1) {
60
- while ((c = str[ptr]) === ' ' || c === '\t' || (!banNewLines && (c === '\n' || c === '\r' && str[ptr + 1] === '\n')))
61
- ptr++;
62
- // Tucking the return statement here would save 5 characters >:)
63
- // But TypeScript fails to detect there is no way to exit the loop so it complains about the lack of final return
64
- if (banComments || c !== '#')
29
+ /** @internal */
30
+ export function skipComment(ctx) {
31
+ for (; ctx.p < ctx.s.length; ctx.p++) {
32
+ let c = ctx.s.charCodeAt(ctx.p);
33
+ if (c === 0xa /* \n */)
34
+ break;
35
+ if (c === 0xd /* \r */ && ctx.s.charCodeAt(ctx.p + 1) === 0xa /* \n */) {
36
+ ctx.p++;
65
37
  break;
66
- ptr = skipComment(str, ptr);
67
- }
68
- return ptr;
69
- }
70
- export function skipUntil(str, ptr, sep, end, banNewLines = false) {
71
- if (!end) {
72
- ptr = indexOfNewline(str, ptr);
73
- return ptr < 0 ? str.length : ptr;
74
- }
75
- for (let i = ptr; i < str.length; i++) {
76
- let c = str[i];
77
- if (c === '#') {
78
- i = indexOfNewline(str, i);
79
- if (i < 0)
80
- break;
81
- }
82
- else if (c === sep) {
83
- return i + 1;
84
38
  }
85
- else if (c === end || (banNewLines && (c === '\n' || (c === '\r' && str[i + 1] === '\n')))) {
86
- return i;
39
+ if ((c < 0x20 && c !== 0x9 /* \t */) || c === 0x7f) {
40
+ TomlError.x('control characters are not allowed in comments', ctx);
87
41
  }
88
42
  }
89
- throw new TomlError('cannot find end of structure', {
90
- toml: str,
91
- ptr: ptr
92
- });
93
43
  }
94
- export function getStringEnd(str, seek) {
95
- let first = str[seek];
96
- let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2]
97
- ? str.slice(seek, seek + 3)
98
- : first;
99
- seek += target.length - 1;
100
- do
101
- seek = str.indexOf(target, ++seek);
102
- while (seek > -1 && first !== "'" && isEscaped(str, seek));
103
- if (seek > -1) {
104
- seek += target.length;
105
- if (target.length > 1) {
106
- if (str[seek] === first)
107
- seek++;
108
- if (str[seek] === first)
109
- seek++;
110
- }
44
+ /** @internal */
45
+ export function skipVoid(ctx, banNewLines, banComments) {
46
+ let c;
47
+ while (ctx.p < ctx.s.length) {
48
+ while (ctx.p < ctx.s.length && ((c = ctx.s.charCodeAt(ctx.p)) === 0x20 ||
49
+ c === 0x9 /* \t */ ||
50
+ (!banNewLines &&
51
+ (c === 0xa /* \n */ || (c === 0xd /* \r */ && ctx.s.charCodeAt(ctx.p + 1) === 0xa /* \n */)))))
52
+ ctx.p++;
53
+ if (banComments || c !== 0x23 /* # */)
54
+ break;
55
+ skipComment(ctx);
111
56
  }
112
- return seek;
113
57
  }
package/dist/version.js CHANGED
@@ -4,4 +4,4 @@
4
4
  // and AGENTS.md on every release. A future improvement would read this from
5
5
  // package.json at build time, but that adds a build-step dependency we
6
6
  // don't want yet.
7
- export const KIMI_PLUGIN_CC_VERSION = "2.0.7";
7
+ export const KIMI_PLUGIN_CC_VERSION = "2.2.0";
package/docs/migration.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Compatibility and upgrades
2
2
 
3
- For plugin 2.0.3, use Kimi Code CLI 2.0.1. The plugin also certifies exact native-v2 versions `0.42.0`, `0.43.0`, `0.43.1`, and `2.0.0` for all eight operations. It does not automatically accept later patches or minor releases.
3
+ For plugin 2.0.7, use Kimi Code CLI 2.1.1. The plugin certifies exact native-v2 versions `0.42.0`, `0.43.0`, `0.43.1`, `2.0.0`, `2.0.1`, `2.0.2`, `2.1.0`, and `2.1.1` for all eight operations. It does not automatically accept later patches or minor releases. The npm distribution adds an installation channel; it does not change this compatibility table.
4
4
 
5
5
  Explicitly pinned legacy-v1 binaries remain supported within `KIMI_TESTED_MINORS`, through 0.41.x, subject to each operation's minimum version. The [runtime table](../runtime/kimi-engine.ts) is the authority.
6
6
 
@@ -8,8 +8,8 @@ Use the [README installation and update steps](../README.md) for Claude Code or
8
8
 
9
9
  ## Plugin versions and upgrades
10
10
 
11
- Plugin versions advance independently using ordinary SemVer. Plugin 2.0.3 certifies
12
- exact Kimi Code CLI 2.0.1; the runtime table records that mapping. Each compatible
11
+ Plugin versions advance independently using ordinary SemVer. Plugin 2.0.7 retains
12
+ the exact CLI certifications from 2.0.6; the runtime table records that mapping. Each compatible
13
13
  maintenance or certification update takes the next unused plugin patch. Published
14
14
  versions and tags are never reused. Every upstream patch requires its own certification.
15
15
  The native-v2 no-plan profile, plugin/marketplace IDs and existing job store are preserved.
@@ -4,6 +4,40 @@ The npm package `kimi-plugin-cc` ships the Claude plugin at its root and the
4
4
  self-contained Codex plugin under `plugins/kimi-codex`. GitHub marketplaces
5
5
  remain available. The package has no install scripts and no separate CLI.
6
6
 
7
+ ## Publishing status
8
+
9
+ As of October 15, 2026, [2.0.8 is live on npm](https://www.npmjs.com/package/kimi-plugin-cc/v/2.0.8), published through
10
+ GitHub Actions OIDC with a signed provenance statement (run 38650044033). 2.0.7 (September 26) was the
11
+ hand-published bootstrap.
12
+ The first upload used the validated GitHub Actions artifact and interactive npm
13
+ authentication. Its public tarball matched that artifact byte-for-byte, and a
14
+ fresh registry install passed isolated setup/check for both hosts.
15
+
16
+ The trusted publisher is configured for `linxule/kimi-plugin-cc`, workflow
17
+ `publish.yml`, environment `npm`, with direct publishing allowed.
18
+ `NPM_TRUSTED_PUBLISHING=true` is enabled in GitHub Actions. Future GitHub releases
19
+ use short-lived OIDC credentials, without a stored npm token or interactive 2FA.
20
+ The first npm OIDC publication will be the next release; do not republish 2.0.7.
21
+
22
+ **2026-10-15 status.** The `v2.0.8` release triggered the first OIDC attempt. It
23
+ failed twice on the publish step for reasons unrelated to the package: current
24
+ npm parses a bare `package/<file>.tgz` argument as the GitHub shorthand
25
+ `github:package/<file>` (fixed in `publish.yml`, which now locates the tarball
26
+ and publishes an anchored `./` path), and the manual re-dispatch then received
27
+ `404 Not Found - PUT https://registry.npmjs.org/kimi-plugin-cc`, the response
28
+ npm gives when the OIDC token exchange is refused. The GitHub Actions side
29
+ checks out (environment `npm` exists with no protection rules;
30
+ `NPM_TRUSTED_PUBLISHING=true`), so the cause was on npmjs.com: the trusted-publisher binding created on
31
+ September 26 had **expired unused** (npm expires a binding that never publishes).
32
+ Deleting it and creating a new one (GitHub Actions, `linxule` / `kimi-plugin-cc` /
33
+ `publish.yml` / environment `npm`, with *Allow npm publish* ticked) and
34
+ dispatching `publish.yml` with `tag=v2.0.8` published 2.0.8 with provenance. A new
35
+ binding shows *Pending validation* until its first publish, which must happen
36
+ within about a day; publish promptly after creating one, and expect to recreate it
37
+ after a long gap between releases. Note that a `release` event runs the workflow
38
+ file at the tag, so fixes to `publish.yml` reach an existing tag only through
39
+ `workflow_dispatch`.
40
+
7
41
  ## Validation
8
42
 
9
43
  Use the repository's normal version bump and generated-surface procedure in
@@ -25,7 +59,7 @@ vendored licenses, and installs/checks the safety hook in isolated homes for
25
59
  both hosts. It also exercises unsupported-version refusal using a fake CLI.
26
60
  It does not invoke a model or read the maintainer's Kimi configuration.
27
61
 
28
- ## First publication
62
+ ## First publication (completed bootstrap reference)
29
63
 
30
64
  The package must first exist on npm before its package settings can be configured.
31
65
  After the release commit passes CI, create its immutable version tag and GitHub
package/docs/safety.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Safety
2
2
 
3
- This guide describes the shipped 2.0.6 runtime. Read-only tasks deny file writes and shell commands. Rescue and pursue can edit your workspace. Write-swarm returns a patch from a temporary Git worktree.
3
+ This guide describes the shipped 2.2.0 runtime. Read-only tasks deny file writes and shell commands. Rescue and pursue can edit your workspace. Write-swarm returns a patch from a temporary Git worktree.
4
4
 
5
5
  Native agent-core-v2 is certified at exact CLI versions `0.42.0`, `0.43.0`, `0.43.1`, `2.0.0`, `2.0.1`, `2.0.2`, `2.1.0`, and `2.1.1`, with plan mode disabled. The [runtime certification table](../runtime/kimi-engine.ts) controls routing. The hook is not an operating-system sandbox.
6
6
 
@@ -74,7 +74,7 @@ Key constraints (verified by [`runtime/hooks/managed-block.ts`](../runtime/hooks
74
74
  3. **TOML basic-string escaping.** The command field is a TOML basic string. Quotes, backslashes, newlines, and control characters in the resolved hook path are escaped during install; paths containing characters that cannot be safely escaped (literal `"`, raw control chars) are rejected up front with `SETUP_HOOK_PATH_UNSAFE`.
75
75
  4. **Exact marker grammar.** The verifier rejects stray comments containing the marker tag, partial references, duplicate blocks (from a setup race), and orphan markers (from a manual edit). The same parser is used by both the installer and the per-call verifier so the two cannot disagree.
76
76
  5. **Exact command equality, per host.** The verifier reconstructs the canonical command byte string from the current env and equality-checks the installed `command` (never a substring — a crafted `true # /path/to/approval-hook.js` runs only `true` and would auto-allow). This holds *per host id* (below): each host verifies its own block.
77
- 6. **Full-config parse and whole-hook-array validation.** The verifier parses the complete file with vendored `smol-toml@1.6.1` with the upstream GHSA-7w5x-hrqm-74c2 fix backported (see `runtime/vendor/smol-toml/README.md`), before inspecting the managed block. It then applies upstream's strict config schema to every configured hook (`event`, optional string `matcher`, non-empty `command`, optional integer `timeout` 1–600, no extra fields). A malformed unrelated TOML value or one invalid foreign hook can make kimi-code discard enforcement; both therefore fail verification.
77
+ 6. **Full-config parse and whole-hook-array validation.** The verifier parses the complete file with vendored `smol-toml@1.9.1` with upstream fixes included and no local JavaScript modifications (see `runtime/vendor/smol-toml/README.md`), before inspecting the managed block. It then applies upstream's strict config schema to every configured hook (`event`, optional string `matcher`, non-empty `command`, optional integer `timeout` 1–600, no extra fields). A malformed unrelated TOML value or one invalid foreign hook can make kimi-code discard enforcement; both therefore fail verification.
78
78
  7. **Serialized, identity-bound setup mutations.** Install and uninstall hold a private `0o600` sibling lock across the complete read-modify-write transaction. The candidate lock is published atomically; lock reads are no-follow, nonblocking, regular-file-only, and capped at 4 KiB. Acquisition is bounded, live owners are never stolen, stale recovery is guarded by a nonrecursive recovery lease, and inode/token ownership is rechecked before config mutation or lock removal. Simultaneous Claude Code/Codex setup runs therefore cannot lose either host's block, and a replaced lock is never treated as ours. A newly created Kimi home is `0o700`; atomic config writes remain `0o600`.
79
79
  8. **Inline hook arrays are normalized before insertion.** kimi-code accepts both `hooks = [...]` and `[[hooks]]`, but appending a managed array-of-tables block after an inline assignment can produce a conflicting document. Setup converts a valid inline hook array to canonical `[[hooks]]` tables before inserting the host block and emits an explicit warning that formatting and comments inside that assignment are normalized. Surrounding config bytes are preserved.
80
80
 
@@ -225,6 +225,53 @@ If another installed host still points to an older hook, that hook may veto thes
225
225
  - **Field-proven by a real-binary smoke.** `tests/runtime/real-binary-smoke.test.ts` runs the real `runSwarm --write` and asserts the POSITIVE end-to-end (a `coder` subagent's edits land in the worktree, captured as a patch; the user's real tree is byte-identical + git-clean; the worktree is cleaned up) plus the NEGATIVE (an absolute-path write outside the trusted root is hook-denied). It is the check that catches `tool_input` field-name drift — `Write`/`Edit` carry `path`, not `file_path`, and reading the wrong key denies every real write silently.
226
226
  - **Model-invocable agent (v1.5), patch-only by construction.** Following the field-proof above, `--write` now has a `kimi-swarm-write` subagent so the main Claude thread can dispatch a write fan-out on its own judgement — the same human-command + model-invocable-agent split the rest of the suite uses. Auto-dispatch is a safe extension here because it widens **no** write surface and removes **no** bound: the agent path runs the identical `runSwarm --write` code, so edits still land only in the throwaway worktree, the managed hook still gates every `coder` subagent, the patch is still handed back for the user to apply (**the plugin never applies or commits**), and `--budget` + default `--max-concurrency` 1 + the hook requirement all still hold. In fact write-swarm is a *less* dangerous agent than the `kimi-pursue` agent already shipped: pursue writes directly into the workspace, whereas write-swarm's output is a patch the human reviews before anything reaches a real tree. Because it is still a write surface, the agent description demands BOTH signals — many disjoint write targets AND explicit fan-out intent — and steers single edits to `kimi-rescue`, read-only fan-outs to `kimi-swarm`, and autonomous loops to `kimi-pursue`. Default `--max-concurrency` stays **1**; raising it (concurrent writers) remains a deliberate, human-or-explicitly-requested choice.
227
227
 
228
+ ## Response budgets and partial output (2.2.0)
229
+
230
+ Every single-turn command runs under a wall-clock budget: ask, review and
231
+ challenge default to 30 minutes, rescue to 45, and `--budget <dur>` (at most
232
+ 24h) overrides it per job. The budget is persisted on the job row, so a detached
233
+ worker and a same-operation resume use the same ceiling. For these commands the
234
+ budget is a response-time limit, not a safety bound: a timeout is a safe
235
+ failure. The job stays `failed`; whatever assistant prose had settled is written
236
+ into the artifact under a heading that says it is NOT a final answer, and
237
+ `result <jobId> --json` reports `partial_output: true` when prose exists.
238
+ Cancellation and nonzero exits do not capture partial output. A session ID is kept if Kimi announced it or, on a native-v2 budget timeout, recovered from the session store when exactly one session matches this spawn — created between spawn and settlement in the same directory AND whose journal opens with this job's exact prompt; `result --json` identifies the source in `kimi_session_id_source`, and `error.details.session_recovery` says why no ID was recovered (`none`, `journal_missing:<n>`, `digest_mismatch:<n>`, `ambiguous:<n>`, `enumeration_capped`, `engine_unobserved`). Recovery is bounded and refuses ambiguity, incomplete enumeration, and IDs already used by another job on this host. The prompt binding is what makes attribution proof rather than probability: a concurrent interactive session in the same directory, another host's finished job (host stores cannot see each other's IDs) or a `state.json` planted by repository code running under rescue all fail it unless they carry the exact prompt, and a planted clean journal could otherwise replay attacker-written turns into the next resume. Recovery is skipped when the run never emitted the `system.version` marker, since resume would refuse the row as lineage-unknown anyway. Recovered sessions still require proven native-v2 lineage and the raw plan-taint scan before resume. Inherited budgets are printed to stderr at launch; status and `result --json` expose `budget_source` (`explicit`, `inherited`, or `default`; old non-null budgets report `unknown`).
239
+
240
+ Pursue and swarm keep their ceilings as the sole hard bound on autonomy and
241
+ cost. One nuance: a run whose process exits cleanly in the same event-loop tick
242
+ as the budget timer is accepted as a success rather than discarded, so a goal
243
+ can complete at most one tick past its ceiling — teardown has already settled by
244
+ then, and nothing runs longer.
245
+
246
+ Every spawn also deletes `KIMI_CODE_TRUST_WORKSPACE` from the child
247
+ environment (kimi-code 2.2.0): a trusted workspace boots repo-defined project
248
+ MCP servers at bootstrap, outside the PreToolUse hook, and an inherited value
249
+ would have trusted every plugin spawn including swarm-write's throwaway
250
+ worktree. A directory the operator trusted interactively in the Kimi TUI still
251
+ has that property; that persisted-trust route is a documented residual, not
252
+ something the plugin can close.
253
+
254
+ ## Running a newer Kimi than the plugin certifies
255
+
256
+ The plugin refuses any kimi-code version it has not certified, so an operator
257
+ who tracks `@latest` loses every `/kimi:*` command until the next certification.
258
+ The clean way to have both is to give the plugin its own binary: install the
259
+ highest certified version somewhere stable and point `KIMI_PLUGIN_CC_KIMI_BIN`
260
+ at it (the runtime resolves that path instead of `kimi` on `PATH`; every spawn
261
+ still exports `KIMI_CODE_NO_AUTO_UPDATE=1`, so the pinned copy never drifts).
262
+
263
+ ```sh
264
+ PIN=~/.local/share/kimi-plugin-cc/kimi-bin/2.2.0
265
+ mkdir -p "$PIN" && cd "$PIN" && printf '{"name":"kimi-plugin-cc-pin","private":true}\n' > package.json
266
+ bun add @moonshot-ai/kimi-code@2.2.0
267
+ export KIMI_PLUGIN_CC_KIMI_BIN="$PIN/node_modules/.bin/kimi" # e.g. in ~/.zshenv
268
+ ```
269
+
270
+ Both binaries share `~/.kimi-code` (login, config, hook), so nothing else
271
+ changes. The certified maximum can be read from the installed plugin
272
+ (`NATIVE_V2_CERTIFIED_VERSIONS` in `dist/kimi-engine.js`), which lets an upgrade
273
+ script bump the pin automatically after each certification.
274
+
228
275
  ## The setup probe
229
276
 
230
277
  `/kimi:setup` runs a two-layer probe before reporting success:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kimi-plugin-cc",
3
- "version": "2.0.7",
3
+ "version": "2.2.0",
4
4
  "description": "Kimi for Claude Code and Codex: code questions, reviews, fixes, and parallel tasks through the local Kimi Code CLI.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -19,7 +19,7 @@
19
19
  "devDependencies": {
20
20
  "@types/bun": "^1.4.2",
21
21
  "@types/node": "^26.5.1",
22
- "smol-toml": "1.8.0",
22
+ "smol-toml": "1.9.1",
23
23
  "tsx": "^4.23.13",
24
24
  "typescript": "^7.0.2"
25
25
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kimi",
3
- "version": "2.0.7",
3
+ "version": "2.2.0",
4
4
  "description": "Codex plugin that delegates review, challenge, ask, rescue, pursue, and swarm workflows to the local kimi-code CLI through the kimi-plugin-cc companion runtime.",
5
5
  "author": {
6
6
  "name": "linxule"