zigmini 0.8.0-beta.6 → 0.8.0-beta.7

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 (49) hide show
  1. package/README.ko.md +77 -19
  2. package/README.md +78 -19
  3. package/assets/agent/gateway-request-rules.md +1 -0
  4. package/assets/agent/gateway-rules.md +6 -0
  5. package/assets/android/zigmini.apk +0 -0
  6. package/assets/android/zigmini.apk.json +3 -3
  7. package/assets/build.json +1 -1
  8. package/docs/manual.ko.md +23 -1
  9. package/docs/manual.md +23 -1
  10. package/host/omo-bridge/task-control.ts +4 -0
  11. package/host/omo-check/patches/omo-5.1.35-compaction-idle.patch +77 -0
  12. package/host/omo-check/patches/omo-5.1.35-compaction-intent.patch +212 -0
  13. package/host/omo-check/patches/omo-5.1.35-compaction-model.patch +299 -0
  14. package/host/omo-check/patches/omo-5.1.35-compaction-preservation.patch +1063 -0
  15. package/host/omo-check/patches/omo-5.1.35-compaction-refusal.patch +83 -0
  16. package/host/omo-check/patches/omo-5.1.35-gateway-port.manifest.json +169 -7
  17. package/host/omo-check/patches/omo-5.1.35-request-rules.patch +109 -0
  18. package/host/omo-check/patches/omo-5.1.35-turn-stats.patch +76 -0
  19. package/package.json +4 -1
  20. package/scripts/secret-rules.ts +101 -0
  21. package/scripts/secret-scan.allow.json +155 -0
  22. package/scripts/secret-scan.rules.json +30 -0
  23. package/src/agent-request-rules.ts +82 -0
  24. package/src/cli/access.ts +31 -34
  25. package/src/cli/launchd.ts +30 -4
  26. package/src/cli/main.ts +3 -43
  27. package/src/cli/messages.ts +160 -0
  28. package/src/cli/pair.ts +32 -25
  29. package/src/cli/setup.ts +164 -75
  30. package/src/decisions.ts +20 -1
  31. package/src/hub.ts +140 -30
  32. package/src/main.ts +43 -2
  33. package/src/omo/client.ts +28 -0
  34. package/src/protocol.ts +30 -1
  35. package/src/reply-notifications.ts +144 -0
  36. package/src/runtime/paths.ts +2 -0
  37. package/src/security-check.ts +241 -0
  38. package/src/store.ts +71 -1
  39. package/src/turn-stats.ts +49 -0
  40. package/src/work-watch.ts +36 -10
  41. package/src/work.ts +29 -4
  42. package/web/app.js +158 -0
  43. package/web/decisions.js +3 -0
  44. package/web/index.html +16 -0
  45. package/web/notifications.d.ts +25 -0
  46. package/web/notifications.js +118 -0
  47. package/web/style.css +12 -0
  48. package/web/turn-stats.d.ts +26 -0
  49. package/web/turn-stats.js +58 -0
@@ -0,0 +1,83 @@
1
+ --- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/refusal.js
2
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/refusal.js
3
+ @@ -25,7 +25,14 @@
4
+ const details = refusalDetails(message);
5
+ return details ? new ClaudeSdkRefusalError(details.content, details.category) : undefined;
6
+ }
7
+ -export function isClaudeSdkRefusal(error) {
8
+ - return error instanceof ClaudeSdkRefusalError;
9
+ -}
10
+ +export function isClaudeSdkRefusal(error) {
11
+ + return getClaudeSdkRefusal(error) !== undefined;
12
+ +}
13
+ +export function getClaudeSdkRefusal(error) {
14
+ + if (error instanceof ClaudeSdkRefusalError)
15
+ + return error;
16
+ + if (error !== null && typeof error === "object" && "original" in error)
17
+ + return getClaudeSdkRefusal(error.original);
18
+ + return undefined;
19
+ +}
20
+ //# sourceMappingURL=refusal.js.map
21
+
22
+ --- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/errors.js
23
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/errors.js
24
+ @@ -1,4 +1,5 @@
25
+ -import { rateLimitModelFamily } from "../../../credential-pool/model-scope.js";
26
+ +import { rateLimitModelFamily } from "../../../credential-pool/model-scope.js";
27
+ +import { isClaudeSdkRefusal } from "./refusal.js";
28
+ const SDK_ERROR_CLASSIFICATIONS = {
29
+ authentication_failed: { kind: "auth_error", retryable: true },
30
+ oauth_org_not_allowed: { kind: "org_not_allowed", retryable: true },
31
+ @@ -75,8 +76,10 @@
32
+ return new Error(text ? (message.error === "unknown" ? text : `${text} (${message.error})`) : message.error);
33
+ }
34
+ /** Classifies Anthropic Subscription error codes and HTTP-shaped fallback text in one place. */
35
+ -export function classifySdkError(error) {
36
+ - const classification = classifyErrorKind(error);
37
+ +export function classifySdkError(error) {
38
+ + if (isClaudeSdkRefusal(error))
39
+ + return OTHER_ERROR;
40
+ + const classification = classifyErrorKind(error);
41
+ if (classification.kind !== "rate_limit")
42
+ return classification;
43
+ const modelFamily = rateLimitModelFamily(errorText(error));
44
+ --- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js
45
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js
46
+ @@ -9,7 +9,7 @@
47
+ import { buildDeferredPromptStream, buildPromptBlocks } from "./prompt-bridge.js";
48
+ import { pinOneShotPromptCacheTtl } from "./prompt-cache-ttl.js";
49
+ import { dedupeUltraworkBlocks } from "./prompt-directive-dedupe.js";
50
+ -import { refusalError } from "./refusal.js";
51
+ +import { getClaudeSdkRefusal, refusalError } from "./refusal.js";
52
+ import { getSdkBoundary, loadClaudeAgentSdk } from "./sdk-boundary.js";
53
+ import { emitContinuityObservation } from "./session-observability.js";
54
+ import { forgetBinding } from "./session-reattach.js";
55
+ @@ -244,8 +244,11 @@
56
+ // senpi's own history instead of resuming that rewritten transcript.
57
+ if (nativeCompactionStopped && options?.sessionId)
58
+ forgetBinding(options.sessionId);
59
+ - output.stopReason = options?.signal?.aborted ? "aborted" : "error";
60
+ - // A failed result still bills its tokens; managed and resident lanes
61
+ + output.stopReason = options?.signal?.aborted ? "aborted" : "error";
62
+ + const refusal = getClaudeSdkRefusal(error);
63
+ + if (refusal)
64
+ + output.stopDetails = { type: "refusal", explanation: refusal.message };
65
+ + // A failed result still bills its tokens; managed and resident lanes
66
+ // throw before the result reaches this loop, so account for it here.
67
+ const billed = sdkResultFailureUsage(error);
68
+ if (billed)
69
+ --- a/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/deterministic-fallback.js
70
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/deterministic-fallback.js
71
+ @@ -97,8 +97,10 @@
72
+ return true;
73
+ return error instanceof SummaryRequestError && !error.transient && !error.refused && error.failureKind === undefined;
74
+ }
75
+ -export function classifyRequiredCompactionFallbackFailure(error) {
76
+ - if (error instanceof UnsafeRetainedSuffixError)
77
+ +export function classifyRequiredCompactionFallbackFailure(error) {
78
+ + if (error instanceof SummaryRequestError && error.refused)
79
+ + return undefined;
80
+ + if (error instanceof UnsafeRetainedSuffixError)
81
+ return "unsafe-retained-content";
82
+ if (error instanceof StreamDurationBudgetError ||
83
+ error instanceof StreamIdleTimeoutError ||
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schema": 1,
3
- "revision": 4,
3
+ "revision": 8,
4
4
  "upstream": {
5
5
  "omo": "5.1.35",
6
6
  "senpi": "2026.10.16"
7
7
  },
8
- "candidate": "<zigmini home>/omo/5.1.35-zgw",
8
+ "candidate": "<zigmini home>/omo/5.1.35-zgw.8",
9
9
  "pristine": "<zigmini home>/omo/5.1.35",
10
10
  "entrypoint": "node_modules/@code-yeongyu/senpi/dist/cli.js",
11
11
  "patchRoot": "node_modules",
@@ -13,18 +13,25 @@
13
13
  "omo-5.1.35-gateway-port.patch",
14
14
  "omo-5.1.35-image-replay.patch",
15
15
  "omo-5.1.35-question-capability.patch",
16
- "omo-5.1.35-phantom-attach.patch"
16
+ "omo-5.1.35-phantom-attach.patch",
17
+ "omo-5.1.35-request-rules.patch",
18
+ "omo-5.1.35-compaction-model.patch",
19
+ "omo-5.1.35-compaction-refusal.patch",
20
+ "omo-5.1.35-compaction-preservation.patch",
21
+ "omo-5.1.35-compaction-intent.patch",
22
+ "omo-5.1.35-compaction-idle.patch",
23
+ "omo-5.1.35-turn-stats.patch"
17
24
  ],
18
25
  "files": [
19
26
  {
20
27
  "path": "node_modules/@code-yeongyu/senpi/dist/core/agent-session.d.ts",
21
28
  "originalSha256": "f824b37b0d19f07e182254d0133cf57526b6a6fde9c8cfad62b6ba5af50cd119",
22
- "patchedSha256": "d669bdf3c71d5bef8d4491c912e83fc4229704b0fccfb42932d8a1f924edc8c6"
29
+ "patchedSha256": "b2457c6da97d27285671cf38de1c2eb48f61a50cbcae6b79b42b0604d9f98bd5"
23
30
  },
24
31
  {
25
32
  "path": "node_modules/@code-yeongyu/senpi/dist/core/agent-session.js",
26
33
  "originalSha256": "d861188c06f5354b8279d3158b8e7db1a7d86053a3dfe3b581c4fef52a1bcbbc",
27
- "patchedSha256": "8843671f6eff07ceaeb7d90cca3617321fe926efd8e1d48941075f9e6e36d761"
34
+ "patchedSha256": "60e22e74fb4b607f3a2fc07fbbe4fd773973414e58cf9fb54dec06c0fd1456cb"
28
35
  },
29
36
  {
30
37
  "path": "node_modules/@code-yeongyu/senpi/dist/core/auth-storage.js",
@@ -41,16 +48,46 @@
41
48
  "originalSha256": "2f6a8601aa879e11e37322d939ebd0f31ed21b51735d0f71f6833ee85d3b1c75",
42
49
  "patchedSha256": "dd2427652a7a1b0bf07a18bddfc9ffc2f48140376eb6349e7ff3c350d508c447"
43
50
  },
51
+ {
52
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/compaction/compaction.js",
53
+ "originalSha256": "1c24c48064542867c58371cf469af8e98984bd44861c464dbd122b79683c4a21",
54
+ "patchedSha256": "655797b41847962657bc355835d1f08e88d06f21c4b00535aa2d693685c72660"
55
+ },
44
56
  {
45
57
  "path": "node_modules/@code-yeongyu/senpi/dist/core/credential-accounts.js",
46
58
  "originalSha256": "f98f1758e934135084d2765f73b20e4c22f6a74dce344569c6dfbfe9dc250cbc",
47
59
  "patchedSha256": "218862c77a0df2691a521ae99c95e99acc64a8547dd6d6e627ebd98369d34fe0"
48
60
  },
61
+ {
62
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts",
63
+ "originalSha256": "6a4536363e0c71add5c61f969f1880503df02249914d294412dcf5b284b58f16",
64
+ "patchedSha256": "7858496cdf2c89533810f310156915f5d3be578a22a81a38de34b8d8d194c8cd"
65
+ },
66
+ {
67
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js",
68
+ "originalSha256": "8fd122e765b0f8f395a1d1c14a41f538a46816b9d68b1404004cb604f17bf038",
69
+ "patchedSha256": "877361fa222bf908c735bc7c8e22028ad6604c36d07c11928c27217d3fb9dad6"
70
+ },
49
71
  {
50
72
  "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/cold-seed-images.js",
51
73
  "originalSha256": "f40ff494a945bc5188dca010476cac76ae60f178f67d5391457a04e7f266c16e",
52
74
  "patchedSha256": "7c042bc1bfc0240cfd576cdc989f174dd248270219cc834788d22c9df3aa6903"
53
75
  },
76
+ {
77
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/errors.js",
78
+ "originalSha256": "e4fce0dbc87e7d9b19844319a5064c4fbc0681db8574279a67e2094285b41934",
79
+ "patchedSha256": "52efe8a368e29e425ba75453870f9c72a50889a60a9fdacdcc9339ac3fb75025"
80
+ },
81
+ {
82
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/refusal.js",
83
+ "originalSha256": "d13ad77fff8128a833494dfd1eb95760bca3f57621c9e3cb36b0370407e58c46",
84
+ "patchedSha256": "1b4673286c40d615752ec4acfd0fdd17e645c3af275232d9c6b8fad2ace10445"
85
+ },
86
+ {
87
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js",
88
+ "originalSha256": "477ec59462bd11ab89b5fcbe018e8f505b7a79a2550aa23f76d0b04e48026d22",
89
+ "patchedSha256": "edf9fc8a5779ddc92d81d879b7a18214e20be0ec7895c481c09d37d47b390892"
90
+ },
54
91
  {
55
92
  "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/ask-user/pending.js",
56
93
  "originalSha256": "0f8156f0cdd1fbc4bb08486d0b8b6ff5f821b9ac5df4cf5056714689fb2bb0c5",
@@ -71,6 +108,116 @@
71
108
  "originalSha256": "2e6cb7dd518e11b49b04275bf2e7d8014c834814b05a7cfc2232d1f970f51df9",
72
109
  "patchedSha256": "61ed013b8e33a3c4e1d4a9c9e491d0193811721a27afba11fb8cebb089cd805e"
73
110
  },
111
+ {
112
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/context-pipeline.js",
113
+ "originalSha256": "9af989447ef4145f25e58fb421a42d98af5a544b5297c1b66f9577ca285c2fe9",
114
+ "patchedSha256": "4d87236638509c1e28b29e6964fbab92f91fa477543ecca48d3bd87c6a422cc7"
115
+ },
116
+ {
117
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/context-reduction-handoff.js",
118
+ "originalSha256": "e02c76847d5060bf9f1527786c0a545143bfc7c5a77ecc194341d48564ebaed1",
119
+ "patchedSha256": "df205aa25d56439e56ac275e6c903e4dccd812e1b5a2aea5450ae645115c2e76"
120
+ },
121
+ {
122
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/context-reduction-lifecycle.js",
123
+ "originalSha256": "8ff27c815d2a33f1afbd23dd800157e3abad4324914d9cd776883122697ba003",
124
+ "patchedSha256": "c27cabce3751e2db42054bf51473673126366ce9aae5ed48dfeeef7066164f50"
125
+ },
126
+ {
127
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/deterministic-fallback.js",
128
+ "originalSha256": "2e33f9e1afc7320070e1566f5ef52a0b6ecb5e6adc59d9a0c4633773e8fb4b14",
129
+ "patchedSha256": "c00f5fadcf0d445a5fbe4e542a2a1b6e1c0dca5509822a65602d0fc32dbdde29"
130
+ },
131
+ {
132
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/emergency-prune.js",
133
+ "originalSha256": "64487d2e21273bd5a735485507e12a10c1039e6ecde548722e8e2c4e4d3b4773",
134
+ "patchedSha256": "00b8ce5b493f8605abc2f96b1270f40e532f4bc94532c3bf06b18baa81d3b2fe"
135
+ },
136
+ {
137
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/extension-wiring.js",
138
+ "originalSha256": "0612dc35d11ccee5b4b630dae9504f829ab81bf0aa4c8eab35d1aba617174a3f",
139
+ "patchedSha256": "c521dfa0dd9b6281ae84cd40e4bbfd2eea50874222a74c89fef97e88a93813d1"
140
+ },
141
+ {
142
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/index.js",
143
+ "originalSha256": "0ad15ac2a16c8ab7edf986b6a4653f9302c1945e66a1a6f48d6b9ca24ed54b7f",
144
+ "patchedSha256": "aadd59a28f75ecd99b375ba2e2ca180b73dc3b6553f35508aec6590304f4e487"
145
+ },
146
+ {
147
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/lane-policy.js",
148
+ "originalSha256": "e701d15afac3e70e880b544e761c378b8a1d7bf7b68f6e21495a010c3bad3406",
149
+ "patchedSha256": "d16359d291f4c1ddce5f5a2c8d48f09e4c0b14418f3ffc5293a2c22814cbb316"
150
+ },
151
+ {
152
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote-convert.js",
153
+ "originalSha256": "a2eae1acead0a92eaddaa9a0bea56902a05bd33656635dd90b8d7fe84d9d24a0",
154
+ "patchedSha256": "cbe0df338fcb246e96c840bf006d6c1ad74bade810850018c9eba2e843997d5c"
155
+ },
156
+ {
157
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote-responses-v2.js",
158
+ "originalSha256": "9f937de797e3bdabaade5cf1b9dea1f87c7919c93edefe142cd8b2453669a7c4",
159
+ "patchedSha256": "7cc4498b874e94457c15affae32026db219a066a449c7175075a1ae2e23f654e"
160
+ },
161
+ {
162
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote-schema.js",
163
+ "originalSha256": "5154b38ef8fa50626edc444d2111ed931dc72bd3616a5845776995012f501e8e",
164
+ "patchedSha256": "c00d15f2f3cad40f48bffab524ce33afa617a3cd85622b235b2cd105bbbd939c"
165
+ },
166
+ {
167
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote-timeout.d.ts",
168
+ "originalSha256": "f31c61df9f81ff3b7bacf4565463fc5f582b604cc40a5cd81b2f72c5fe2015db",
169
+ "patchedSha256": "63f2cac427f164e65feb58fcd413d6b09f6c4010d8b176971ee716f3a75201bc"
170
+ },
171
+ {
172
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote-timeout.js",
173
+ "originalSha256": "357781e9fc41f840c2048958a784be075595c1dce9c5cf716940bdfe771681d1",
174
+ "patchedSha256": "93702b16d3e351c812631cf541c175c85a8be1ba2515e8eedad08f54c4bdfcc1"
175
+ },
176
+ {
177
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote.d.ts",
178
+ "originalSha256": "d4ea95dbd8f857dfeb5120e5bec35865d78cbb79fe4d59f5cae2ffbe4baa1e66",
179
+ "patchedSha256": "b4873967e45f53cf0647301a486176263e6a5db0843028002990d7f192b443cf"
180
+ },
181
+ {
182
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote.js",
183
+ "originalSha256": "889de1c53e7fc891a099136866c831bd5dfe8ecc15b2dda8d354657e0ec1377b",
184
+ "patchedSha256": "657c9132ab806490c8a07fdc09639621ba7e63a2c1cf93b6bdf0484d896e02f7"
185
+ },
186
+ {
187
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/prompts.js",
188
+ "originalSha256": "5bf074aea446baa51204f29ec00758a93a9cbfc89caa62ffe0a4078dcca0e6b3",
189
+ "patchedSha256": "2178bdcd1f9acfadc07815309c44a8b35741f1243543333795b5e9e64403a197"
190
+ },
191
+ {
192
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/rejected-recovery.d.ts",
193
+ "originalSha256": "40e74e75a77ac0f89ef931385e0f88c3a5d188e2aed06ac295ddfac598ea4464",
194
+ "patchedSha256": "54c5c03818a56d0e6560dcba1fca3bc5163dd29205ddb874f2d5e74f2e76473a"
195
+ },
196
+ {
197
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/rejected-recovery.js",
198
+ "originalSha256": "dccd1cba630ed1674343fae06b21bc081712668481969692ec6e4a246107ca22",
199
+ "patchedSha256": "d6fa0fd69d6532647a3f7db7a35cde499a10fc833e593c5dca96e5e49a4476f5"
200
+ },
201
+ {
202
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/speculative-summary.js",
203
+ "originalSha256": "25dc617e8b999f7b38e638287df81790768279aa78a607e692ccd2b108b88ada",
204
+ "patchedSha256": "ea31110fdc12aee77dc47114efd9d7d230443b735cef300f60e03a27bfbe4779"
205
+ },
206
+ {
207
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/speculative.d.ts",
208
+ "originalSha256": "826ec46e212f2378a68bf55fa2ba1b406d6a225e81549983361cf54480388f32",
209
+ "patchedSha256": "99d2702cf752b45c1d46ffbbf231a9bc1007cdeb919571f7d497b01cada7f931"
210
+ },
211
+ {
212
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/speculative.js",
213
+ "originalSha256": "99ac70e0088f2b66538b76c4b2acef5c13e5290e3e1645fea71b8a70c41799b4",
214
+ "patchedSha256": "a41786f41d838c679f8be6e14d8831458c7acb9d43e5aa44d7030e22470b47e3"
215
+ },
216
+ {
217
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/task-intent.js",
218
+ "originalSha256": "3d78e7e232c172a57af0e919cf0b0b34c98282f235c2d400e4c0bba7f47d545d",
219
+ "patchedSha256": "85a396201b2b248b74cd9eebb362bd96c713806577b179df8ad99c4ac095889f"
220
+ },
74
221
  {
75
222
  "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/permission-system/events.d.ts",
76
223
  "originalSha256": "8fb12fba44abc0066360248855951833eca3684b0d340579db3409afe8a2c767",
@@ -106,10 +253,20 @@
106
253
  "originalSha256": "78865611f2018bbd8bcde160bcb7c517e743394cb0c0e0a3f774e55cd7f25b2f",
107
254
  "patchedSha256": "11f5b99fbd59963d7426e659c6db4764580d7c89aee8bb3a7d1beab12fba9b72"
108
255
  },
256
+ {
257
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/runner.js",
258
+ "originalSha256": "5bbc9de01f7ec0f4d52ade362c9acba8a27d14ecaef4af06593b04451ce9ebe3",
259
+ "patchedSha256": "d1fab450b98c832239424fbffa616690678e44527e36ce6299a300fccb123778"
260
+ },
109
261
  {
110
262
  "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/types.d.ts",
111
263
  "originalSha256": "990653c416a88b76c5338b75cb58e2d8843b4ca528722571b18ef1fdb9d10cc2",
112
- "patchedSha256": "5a646947a3573698bf956cec3395cfc362903fa100ba0920d80e6095add54148"
264
+ "patchedSha256": "4c914b28970084777b31da70f1199c1a744226c6e7beba516bcfa7cb9d3f011a"
265
+ },
266
+ {
267
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/model-runtime.js",
268
+ "originalSha256": "e43ff1363e3284a5b3495d5eaaf4b9b6bcefcd6a60f2f8e7f91b98e1ce69c446",
269
+ "patchedSha256": "350688afcb9da83938130eceda83f033a8cbc7c318e8e44886396826d967ea21"
113
270
  },
114
271
  {
115
272
  "path": "node_modules/@code-yeongyu/senpi/dist/modes/rpc/client-input-handler.js",
@@ -164,12 +321,17 @@
164
321
  {
165
322
  "path": "node_modules/@earendil-works/pi-ai/dist/types.d.ts",
166
323
  "originalSha256": "5440ea974e52f4413f38240dc3f6c435e5e90796e7477fc5de6a500c3c2226c7",
167
- "patchedSha256": "64f7fee8c3ddff79facba8dd5eff14777b1aebef188899aca86379e54cfb9656"
324
+ "patchedSha256": "f8ebe662ba115f2e31b30c2b32d8e3d46581440a87b56c5acf50f853649ea4a4"
168
325
  },
169
326
  {
170
327
  "path": "node_modules/omo-ai/bin/lib/package-paths.js",
171
328
  "originalSha256": "da57bd6016ab85a0f65f53cde53ca3b2e8606e2efc515bcb592d637c24401e67",
172
329
  "patchedSha256": "b99f4ea56abecc7950f06c67fb0f538148e9baae4027c9b55f5debf130479686"
330
+ },
331
+ {
332
+ "path": "node_modules/@code-yeongyu/senpi/dist/core/extensions/builtin/tps.js",
333
+ "originalSha256": "c1a22d2520e84f136aa91a770eb49bd42cfbcce57864bfa263e491f45bb08f65",
334
+ "patchedSha256": "c72d51910bf8e61452cc11d57702075be96d3b5e51c00c30678eb499f2551509"
173
335
  }
174
336
  ]
175
337
  }
@@ -0,0 +1,109 @@
1
+ --- a/@code-yeongyu/senpi/dist/core/model-runtime.js
2
+ +++ b/@code-yeongyu/senpi/dist/core/model-runtime.js
3
+ @@ -72,7 +72,8 @@
4
+ ...options,
5
+ onPayload: async (payload, providerModel) => await onPayload(payload, providerModel, {
6
+ model,
7
+ - headers: options.headers ?? {},
8
+ + headers: options.headers ?? {},
9
+ + streamKind: options.streamKind,
10
+ }),
11
+ };
12
+ }
13
+ --- a/@earendil-works/pi-ai/dist/types.d.ts
14
+ +++ b/@earendil-works/pi-ai/dist/types.d.ts
15
+ @@ -84,9 +84,10 @@
16
+ /** Effective model and fully transformed headers for a request payload hook. */
17
+ export type ProviderRequestMetadata = {
18
+ model: Model<Api>;
19
+ - headers: ProviderHeaders;
20
+ -};
21
+ -export type FetchFunction = typeof globalThis.fetch;
22
+ + headers: ProviderHeaders;
23
+ + streamKind?: "main" | "auxiliary";
24
+ +};
25
+ +export type FetchFunction = typeof globalThis.fetch;
26
+ export interface ProviderResponse {
27
+ status: number;
28
+ headers: Record<string, string>;
29
+ --- a/@code-yeongyu/senpi/dist/core/extensions/runner.js
30
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/runner.js
31
+ @@ -1614,7 +1614,7 @@
32
+ const event = {
33
+ type: "before_provider_request",
34
+ payload: currentPayload,
35
+ - ...(request ? { model: request.model, headers: request.headers } : {}),
36
+ + ...(request ? { model: request.model, headers: request.headers, streamKind: request.streamKind } : {}),
37
+ };
38
+ const handlerResult = await handler(event, this.createContext(ext.path));
39
+ if (handlerResult !== undefined) {
40
+ --- a/@code-yeongyu/senpi/dist/core/extensions/types.d.ts
41
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/types.d.ts
42
+ @@ -1137,10 +1137,11 @@
43
+ /** Effective request model after auth/base-url/upstream-model resolution. */
44
+ model?: Model<Api>;
45
+ /** Final header transform output for this request. Values are never persisted. */
46
+ - headers?: ProviderHeaders;
47
+ -}
48
+ -/**
49
+ - * Fired after request headers are assembled, before the provider HTTP call.
50
+ + headers?: ProviderHeaders;
51
+ + streamKind?: "main" | "auxiliary";
52
+ +}
53
+ +/**
54
+ + * Fired after request headers are assembled, before the provider HTTP call.
55
+ * Handlers mutate `headers` in place (e.g. to inject tracing/session headers);
56
+ * the return value is ignored. A `null` value deletes that header.
57
+ */
58
+ --- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js
59
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js
60
+ @@ -74,7 +74,7 @@
61
+ claudeCodeRun = resolveClaudeCodeRun(defaultExecutableDeps());
62
+ const executable = claudeCodeRun.executable;
63
+ let oneShotCacheTtl;
64
+ - const buildOptions = (authLane) => {
65
+ + const buildOptions = async (authLane) => {
66
+ const queryOptions = buildAnthropicSubscriptionQueryOptions({
67
+ model,
68
+ context,
69
+ @@ -97,7 +97,8 @@
70
+ if (!useResidentSession) {
71
+ oneShotCacheTtl = pinOneShotPromptCacheTtl(queryOptions, authLane ?? providerSettings.tokenInjection ?? "ambient", { ...process.env, ...options?.env });
72
+ }
73
+ - return queryOptions;
74
+ + const replacement = await options?.onPayload?.(queryOptions, model);
75
+ + return replacement === undefined ? queryOptions : replacement;
76
+ };
77
+ const recordContinuity = (observation) => {
78
+ // Not a failure: carry the observation as details only, with no synthesized error.
79
+ --- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js
80
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js
81
+ @@ -186,7 +186,7 @@
82
+ }
83
+ // Lane-owned variables from buildOptions (the compaction-owner overlay) win over the host.
84
+ const ambientAttempt = async () => {
85
+ - const options = input.buildOptions("ambient");
86
+ + const options = await input.buildOptions("ambient");
87
+ options.env = { ...ambientEnvironment, ...options.env };
88
+ return createAttemptMessages(input, { accountName: "ambient", accounts: [], authLane: "ambient", options });
89
+ };
90
+ @@ -215,7 +215,7 @@
91
+ ...(input.model === undefined ? {} : { model: input.model }),
92
+ }),
93
+ runAttempt: async (slot) => {
94
+ - const options = input.buildOptions(pool.lane);
95
+ + const options = await input.buildOptions(pool.lane);
96
+ const accounts = pool.accounts.map((account) => ({ ...account }));
97
+ const prepared = await prepareSlot(pool, slot, signal);
98
+ options.env = { ...prepared.env, ...options.env };
99
+ --- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts
100
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts
101
+ @@ -18,7 +18,7 @@
102
+ export type AuthenticatedQueryInput = {
103
+ prompt: Parameters<SdkQuery>[0]["prompt"];
104
+ query: SdkQuery;
105
+ - buildOptions: (lane: AnthropicSubscriptionTokenInjection) => Options;
106
+ + buildOptions: (lane: AnthropicSubscriptionTokenInjection) => Options | Promise<Options>;
107
+ providerSettings: AnthropicSubscriptionProviderSettings;
108
+ /** Effective request auth environment; overrides the host for account discovery and SDK spawn. */
109
+ env?: Record<string, string>;
@@ -0,0 +1,76 @@
1
+ --- a/@code-yeongyu/senpi/dist/core/extensions/builtin/tps.js
2
+ +++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/tps.js
3
+ @@ -23,6 +23,8 @@
4
+ let lastDeltaMs = null;
5
+ let generationMs = 0;
6
+ let streamedOutput = 0;
7
+ + let turnId = null;
8
+ + let completedMessages = [];
9
+ const finishActiveAssistantTiming = () => {
10
+ if (activeAssistantStartMs === null)
11
+ return;
12
+ @@ -41,7 +43,11 @@
13
+ lastDeltaMs = null;
14
+ thinkingStreamed = false;
15
+ };
16
+ + pi.on("agent_start", (_event, ctx) => {
17
+ + turnId = ctx.mode === "rpc" ? crypto.randomUUID() : null;
18
+ + completedMessages = [];
19
+ + if (turnId !== null)
20
+ + pi.rpc.emit("zgw.turn_started", { version: 1, turnId });
21
+ - pi.on("agent_start", () => {
22
+ activeAssistantStartMs = null;
23
+ assistantElapsedMs = 0;
24
+ generationMs = 0;
25
+ @@ -76,6 +82,7 @@
26
+ // counted in usage.output may be hidden or only summarised after it finished, so it was generated
27
+ // before the first delta. Such a message (usage.reasoning, or a thinking block whose text never
28
+ // streamed: Bedrock and omitted display report no reasoning count) is timed from its start.
29
+ + completedMessages.push(event.message);
30
+ const output = event.message.usage?.output ?? 0;
31
+ const windowStart = reasonedBeforeFirstDelta(event.message, thinkingStreamed) ? messageStartMs : firstDeltaMs;
32
+ const counted = event.message.stopReason !== "aborted" && event.message.stopReason !== "error" && output > 0;
33
+ @@ -90,6 +97,43 @@
34
+ const elapsedMs = assistantElapsedMs;
35
+ const measuredGenerationMs = generationMs;
36
+ const measuredOutput = streamedOutput;
37
+ + // Structured RPC metadata stays out of messages and extension_ui_request notifications.
38
+ + const completedTurnId = turnId;
39
+ + turnId = null;
40
+ + const messages = completedMessages;
41
+ + completedMessages = [];
42
+ + const finalMessage = messages.at(-1);
43
+ + const lastAssistant = event.messages.findLast(isAssistantMessage);
44
+ + if (ctx.mode === "rpc" && completedTurnId !== null && !event.aborted && !event.willRetry && !event.abortSource && !ctx.signal?.aborted
45
+ + && finalMessage && (finalMessage.stopReason === "stop" || finalMessage.stopReason === "length")
46
+ + && Number.isInteger(finalMessage.timestamp) && finalMessage.timestamp > 0
47
+ + && lastAssistant?.timestamp === finalMessage.timestamp
48
+ + && !messages.some((message) => message.stopReason === "error" || message.stopReason === "aborted")) {
49
+ + const stats = {};
50
+ + const hasOutput = messages.some((message) => Number.isFinite(message.usage?.output) && message.usage.output > 0);
51
+ + if (hasOutput) {
52
+ + if (Number.isFinite(elapsedMs) && elapsedMs > 0)
53
+ + stats.modelDurationMs = elapsedMs;
54
+ + const rate = measuredOutput / (measuredGenerationMs / 1000);
55
+ + if (measuredGenerationMs >= MIN_GENERATION_MS && Number.isFinite(rate) && rate > 0)
56
+ + stats.tokensPerSecond = rate;
57
+ + // Unknown usage must not look like a measured zero-percent cache hit.
58
+ + if (messages.every((message) => [message.usage?.input, message.usage?.cacheRead, message.usage?.cacheWrite]
59
+ + .every((value) => Number.isFinite(value) && value >= 0))) {
60
+ + let promptTokens = 0;
61
+ + let cacheRead = 0;
62
+ + for (const message of messages) {
63
+ + promptTokens += message.usage.input + message.usage.cacheRead + message.usage.cacheWrite;
64
+ + cacheRead += message.usage.cacheRead;
65
+ + }
66
+ + const hit = cacheRead / promptTokens * 100;
67
+ + if (Number.isFinite(promptTokens) && Number.isFinite(cacheRead) && Number.isFinite(hit) && hit >= 0 && hit <= 100)
68
+ + stats.cacheHitPercent = hit;
69
+ + }
70
+ + }
71
+ + if (Object.keys(stats).length > 0)
72
+ + pi.rpc.emit("zgw.turn_stats", { version: 1, turnId: completedTurnId, finalMessageTimestamp: finalMessage.timestamp, finalMessageIndex: messages.length, stats });
73
+ + }
74
+ activeAssistantStartMs = null;
75
+ assistantElapsedMs = 0;
76
+ generationMs = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigmini",
3
- "version": "0.8.0-beta.6",
3
+ "version": "0.8.0-beta.7",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zigmini": "bin/zigmini"
@@ -21,6 +21,9 @@
21
21
  "!host/omo-check/*.ts",
22
22
  "!host/omo-check/patches/README.md",
23
23
  "assets/",
24
+ "scripts/secret-rules.ts",
25
+ "scripts/secret-scan.rules.json",
26
+ "scripts/secret-scan.allow.json",
24
27
  "docs/manual.md",
25
28
  "docs/manual.ko.md",
26
29
  "LICENSE.md",
@@ -0,0 +1,101 @@
1
+ // Shared pure matching engine for tracked files, Git objects and release artifacts.
2
+ export type Severity = "secret" | "personal";
3
+ export interface Rule {
4
+ readonly id: string;
5
+ readonly severity: Severity;
6
+ readonly pattern: string;
7
+ readonly flags?: string;
8
+ readonly group?: number;
9
+ readonly minEntropy?: number;
10
+ }
11
+ export interface CompiledRule extends Rule { readonly regex: RegExp }
12
+ export interface AllowEntry {
13
+ readonly value?: string;
14
+ readonly cidr?: string;
15
+ readonly domain?: string;
16
+ /** Exact source paths, or directory prefixes ending in /; omitted for public/reserved values. */
17
+ readonly files?: readonly string[];
18
+ readonly rule?: string;
19
+ readonly reason: string;
20
+ }
21
+ export interface Hit { readonly rule: string; readonly severity: Severity; readonly value: string }
22
+
23
+ export function entropy(value: string): number {
24
+ const counts = new Map<string, number>();
25
+ for (const ch of value) counts.set(ch, (counts.get(ch) ?? 0) + 1);
26
+ let bits = 0;
27
+ for (const n of counts.values()) {
28
+ const p = n / value.length;
29
+ bits -= p * Math.log2(p);
30
+ }
31
+ return bits;
32
+ }
33
+
34
+ export function compileRules(rules: readonly Rule[]): CompiledRule[] {
35
+ return rules.map((rule) => {
36
+ if (!rule.id || (rule.severity !== "secret" && rule.severity !== "personal")) throw new Error(`secret_scan_rule_invalid:${rule.id}`);
37
+ try {
38
+ return { ...rule, regex: new RegExp(rule.pattern, `${rule.flags ?? ""}g`) };
39
+ } catch {
40
+ throw new Error(`secret_scan_rule_invalid:${rule.id}`);
41
+ }
42
+ });
43
+ }
44
+
45
+ export function validateAllow(entries: readonly AllowEntry[]): AllowEntry[] {
46
+ return entries.map((entry, index) => {
47
+ const kinds = [entry.value, entry.cidr, entry.domain].filter((kind) => kind !== undefined).length;
48
+ if (kinds !== 1 || typeof entry.reason !== "string" || entry.reason.trim() === "") throw new Error(`secret_scan_allow_invalid:${index}`);
49
+ return entry;
50
+ });
51
+ }
52
+
53
+ export function ipv4ToInt(ip: string): number | null {
54
+ const parts = ip.split(".");
55
+ if (parts.length !== 4) return null;
56
+ let value = 0;
57
+ for (const part of parts) {
58
+ if (!/^\d{1,3}$/.test(part) || Number(part) > 255) return null;
59
+ value = value * 256 + Number(part);
60
+ }
61
+ return value;
62
+ }
63
+
64
+ export function inCidr(ip: string, cidr: string): boolean {
65
+ const [base, bits] = cidr.split("/");
66
+ const address = ipv4ToInt(ip);
67
+ const network = ipv4ToInt(base ?? "");
68
+ const prefix = Number(bits);
69
+ if (address === null || network === null || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) return false;
70
+ const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
71
+ return ((address & mask) >>> 0) === ((network & mask) >>> 0);
72
+ }
73
+
74
+ export function isAllowed(rule: string, value: string, allow: readonly AllowEntry[], file?: string): boolean {
75
+ for (const entry of allow) {
76
+ if (entry.rule !== undefined && entry.rule !== rule) continue;
77
+ if (entry.files !== undefined && (file === undefined || !entry.files.some((path) => path.endsWith("/") ? file.startsWith(path) : file === path))) continue;
78
+ if (entry.value !== undefined && (entry.value === value || (rule === "email" && entry.value.toLowerCase() === value.toLowerCase()))) return true;
79
+ if (entry.cidr !== undefined && rule === "ipv4" && inCidr(value, entry.cidr)) return true;
80
+ if (entry.domain !== undefined && rule === "email") {
81
+ const host = value.slice(value.lastIndexOf("@") + 1).toLowerCase();
82
+ const domain = entry.domain.toLowerCase();
83
+ if (host === domain || host.endsWith(`.${domain}`)) return true;
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+
89
+ export function scanLine(text: string, rules: readonly CompiledRule[], allow: readonly AllowEntry[], file?: string): Hit[] {
90
+ const hits: Hit[] = [];
91
+ for (const rule of rules) {
92
+ for (const match of text.matchAll(rule.regex)) {
93
+ const value = match[rule.group ?? 0];
94
+ if (!value) continue;
95
+ if (rule.minEntropy !== undefined && entropy(value) < rule.minEntropy) continue;
96
+ if (isAllowed(rule.id, value, allow, file)) continue;
97
+ hits.push({ rule: rule.id, severity: rule.severity, value });
98
+ }
99
+ }
100
+ return hits;
101
+ }