zigmini 0.9.0-beta.2 → 1.0.0-beta.1
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.
- package/README.ko.md +35 -5
- package/README.md +35 -5
- package/assets/agent/gateway-rules.md +1 -1
- package/assets/android/zigmini.apk +0 -0
- package/assets/android/zigmini.apk.json +3 -3
- package/assets/build.json +1 -1
- package/docs/manual.ko.md +51 -11
- package/docs/manual.md +52 -12
- package/host/omo-bridge/agent-control.ts +274 -0
- package/host/omo-bridge/bridge.ts +1 -1
- package/host/omo-bridge/continuity-control.ts +1 -1
- package/host/omo-bridge/model-control.ts +15 -3
- package/host/omo-bridge/task-control.ts +26 -16
- package/host/omo-check/patches/omo-5.1.48-compaction-recovery.patch +912 -0
- package/host/omo-check/patches/omo-5.1.48-gateway-port.manifest.json +12 -11
- package/host/omo-check/patches/omo-5.1.51-compaction-long-context.patch +92 -0
- package/host/omo-check/patches/omo-5.1.51-compaction-port.patch +2635 -0
- package/host/omo-check/patches/omo-5.1.51-compaction-recovery.patch +911 -0
- package/host/omo-check/patches/omo-5.1.51-gateway-port.manifest.json +363 -0
- package/host/omo-check/patches/omo-5.1.51-provider-failover.patch +50 -0
- package/host/omo-check/patches/omo-5.1.51-provider-port.patch +441 -0
- package/host/omo-check/patches/omo-5.1.51-rpc-port.patch +799 -0
- package/host/omo-check/patches/omo-5.1.51-runtime-compat.patch +31 -0
- package/package.json +3 -2
- package/src/agent/api.ts +215 -0
- package/src/agent/definition.ts +177 -0
- package/src/agent/fields.ts +42 -0
- package/src/agent/files.ts +192 -0
- package/src/agent/legacy-files.ts +46 -0
- package/src/agent/list.ts +21 -0
- package/src/agent-rules.ts +14 -5
- package/src/approvals.ts +6 -1
- package/src/cli/main.ts +33 -0
- package/src/cli/messages.ts +10 -2
- package/src/cli/rotate-main.ts +2 -1
- package/src/cli/scan.ts +414 -0
- package/src/cli/security-import.ts +155 -0
- package/src/compact.ts +22 -0
- package/src/decisions.ts +6 -4
- package/src/engine/compat.json +3 -3
- package/src/engine/index.ts +1 -0
- package/src/engine/schema-guard.ts +12 -4
- package/src/engine/sdk.ts +8 -0
- package/src/formal-verification.ts +366 -0
- package/src/gc.ts +37 -6
- package/src/hub.ts +525 -85
- package/src/main.ts +95 -9
- package/src/maintenance.ts +1 -1
- package/src/manual.ts +9 -2
- package/src/omo/client.ts +2 -0
- package/src/profile-prompt.ts +73 -0
- package/src/profile.ts +33 -10
- package/src/protocol.ts +11 -2
- package/src/question-notifications.ts +4 -1
- package/src/reply-notifications.ts +7 -1
- package/src/repo-security-setting.ts +33 -0
- package/src/runtime/paths.ts +16 -0
- package/src/security/baseline.ts +170 -0
- package/src/security/dex-strings.ts +242 -0
- package/src/security/repo-scan.ts +369 -0
- package/src/security/revision-probe.ts +144 -0
- package/src/security/terms.ts +97 -0
- package/src/security-allow.ts +1 -0
- package/src/security-baseline.ts +113 -0
- package/src/security-check.ts +91 -16
- package/src/server-text.ts +38 -0
- package/src/store.ts +142 -16
- package/src/transcript.ts +7 -1
- package/src/ui-language.ts +33 -0
- package/src/update/check.ts +7 -3
- package/src/update/handoff-system.ts +24 -16
- package/src/update/handoff.ts +23 -12
- package/src/update/helper.ts +72 -36
- package/src/update/request.ts +46 -30
- package/src/update/state.ts +21 -11
- package/src/update/strategy.ts +12 -5
- package/src/update/target.ts +32 -15
- package/src/usage.ts +43 -20
- package/src/version.ts +39 -16
- package/src/voice/agents.ts +8 -3
- package/src/voice/install.ts +29 -18
- package/src/work-watch.ts +40 -18
- package/web/agent-icon.js +15 -9
- package/web/agents.d.ts +201 -0
- package/web/agents.js +813 -0
- package/web/app-download.js +32 -19
- package/web/app.js +1407 -278
- package/web/autocopy.js +6 -1
- package/web/boot.js +27 -8
- package/web/commands.js +20 -11
- package/web/decisions.js +70 -49
- package/web/devlog.js +25 -19
- package/web/diagram.js +8 -3
- package/web/enroll.js +43 -22
- package/web/gc.js +32 -19
- package/web/i18n/agent-icon.js +24 -0
- package/web/i18n/agents.js +48 -0
- package/web/i18n/app-download.js +26 -0
- package/web/i18n/autocopy.js +5 -0
- package/web/i18n/boot.js +24 -0
- package/web/i18n/commands.js +34 -0
- package/web/i18n/decisions.js +24 -0
- package/web/i18n/devlog.js +60 -0
- package/web/i18n/diagram.js +12 -0
- package/web/i18n/enroll.js +22 -0
- package/web/i18n/gc.js +38 -0
- package/web/i18n/lastsent.js +10 -0
- package/web/i18n/main.d.ts +4 -0
- package/web/i18n/main.js +527 -0
- package/web/i18n/manual.d.ts +4 -0
- package/web/i18n/manual.js +14 -0
- package/web/i18n/memo.js +30 -0
- package/web/i18n/notifications.js +16 -0
- package/web/i18n/profile.js +13 -0
- package/web/i18n/reference.js +26 -0
- package/web/i18n/security-inbox.js +40 -0
- package/web/i18n/slash.js +38 -0
- package/web/i18n/turn-stats.js +5 -0
- package/web/i18n/update.js +42 -0
- package/web/i18n/voice.js +28 -0
- package/web/i18n/wake.js +16 -0
- package/web/i18n/workid.js +10 -0
- package/web/i18n/workstatus.js +20 -0
- package/web/i18n.d.ts +20 -0
- package/web/i18n.js +63 -0
- package/web/index.html +156 -96
- package/web/lastsent.js +9 -4
- package/web/manual.d.ts +1 -1
- package/web/manual.js +11 -5
- package/web/memo.js +18 -13
- package/web/notifications.js +12 -7
- package/web/profile.js +42 -17
- package/web/reference.js +20 -15
- package/web/security-inbox.d.ts +18 -1
- package/web/security-inbox.js +173 -4
- package/web/slash.js +23 -13
- package/web/style.css +132 -3
- package/web/tooltip.js +5 -2
- package/web/turn-stats.js +6 -2
- package/web/update.js +31 -23
- package/web/voice.js +18 -8
- package/web/wake.d.ts +4 -0
- package/web/wake.js +7 -3
- package/web/workid.js +12 -2
- package/web/workstatus.js +36 -32
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
--- a/@code-yeongyu/senpi/dist/core/auth-storage.js
|
|
2
|
+
+++ b/@code-yeongyu/senpi/dist/core/auth-storage.js
|
|
3
|
+
@@ -319,7 +319,6 @@ export class InMemoryAuthStorageBackend {
|
|
4
|
+
*/
|
|
5
|
+
export class AuthStorage {
|
|
6
|
+
constructor(storage, authPath) {
|
|
7
|
+
- this.data = {};
|
|
8
|
+
this.runtimeOverrides = new Map();
|
|
9
|
+
this.extensionOAuthProviders = new Map();
|
|
10
|
+
this.errors = [];
|
|
11
|
+
@@ -342,7 +341,6 @@ export class AuthStorage {
|
|
12
|
+
// Another instance already loaded this store: start from its credentials, not an
|
|
13
|
+
// empty snapshot, so a busy read below keeps them. A repaired or migrated load
|
|
14
|
+
// leaves the revision unset, so only an exact revision match skips the re-read.
|
|
15
|
+
- this.data = this.readState.data;
|
|
16
|
+
this.dataLoaded = true;
|
|
17
|
+
const revision = getFileContentRevision(authPath);
|
|
18
|
+
if (revision !== undefined && revision === this.readState.revision)
|
|
19
|
+
@@ -350,6 +348,14 @@ export class AuthStorage {
|
|
20
|
+
}
|
|
21
|
+
this.reload();
|
|
22
|
+
}
|
|
23
|
+
+ // Synchronous extension reads and async runtime reads share one snapshot.
|
|
24
|
+
+ get data() {
|
|
25
|
+
+ return this.readState.data;
|
|
26
|
+
+ }
|
|
27
|
+
+ set data(data) {
|
|
28
|
+
+ this.readState.data = data;
|
|
29
|
+
+ this.readState.revision = undefined;
|
|
30
|
+
+ }
|
|
31
|
+
static create(authPath = join(getAgentDir(), "auth.json")) {
|
|
32
|
+
const normalizedAuthPath = normalizePath(authPath);
|
|
33
|
+
return new AuthStorage(new FileAuthStorageBackend(normalizedAuthPath), normalizedAuthPath);
|
|
34
|
+
@@ -420,7 +426,6 @@ export class AuthStorage {
|
|
35
|
+
return backupPath;
|
|
36
|
+
}
|
|
37
|
+
updateReadState(data, revision) {
|
|
38
|
+
- this.data = data;
|
|
39
|
+
this.dataLoaded = true;
|
|
40
|
+
this.dataFromBusyRead = false;
|
|
41
|
+
this.readState.data = data;
|
|
42
|
+
@@ -877,4 +882,4 @@ export function readStoredCredential(providerId, authPath = join(getAgentDir(),
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
-//# sourceMappingURL=auth-storage.js.map
|
|
47
|
+
|
|
48
|
+
+//# sourceMappingURL=auth-storage.js.map
|
|
49
|
+
--- a/@code-yeongyu/senpi/dist/core/credential-accounts.js
|
|
50
|
+
+++ b/@code-yeongyu/senpi/dist/core/credential-accounts.js
|
|
51
|
+
@@ -75,6 +75,8 @@ function blockedModels(now, ...sources) {
|
|
52
|
+
* here without auth.json ever carrying block state.
|
|
53
|
+
*/
|
|
54
|
+
export async function getCredentialAccounts(storage, provider, env = process.env, repository) {
|
|
55
|
+
+ // Refresh metadata without resolving keys or applying runtime overrides.
|
|
56
|
+
+ await storage.list();
|
|
57
|
+
return summarizeCredentialAccounts(provider, storage.get(provider), env, repository ?? credentialPoolRepository(storage));
|
|
58
|
+
}
|
|
59
|
+
/** Storage-free variant for callers that already hold the credential (e.g. auth check). */
|
|
60
|
+
@@ -211,4 +213,4 @@ export async function removeCredentialAccount(storage, provider, name, env = pro
|
|
61
|
+
await repo.mutateSlotState(provider, "stored", name, () => undefined);
|
|
62
|
+
emitProviderAccountsChanged(provider);
|
|
63
|
+
}
|
|
64
|
+
-//# sourceMappingURL=credential-accounts.js.map
|
|
65
|
+
|
|
66
|
+
+//# sourceMappingURL=credential-accounts.js.map
|
|
67
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts
|
|
68
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts
|
|
69
|
+
@@ -9,7 +9,7 @@ export type { AuthenticatedAttemptInput } from "./auth-attempt.ts";
|
|
70
|
+
export type AuthenticatedQueryInput = {
|
|
71
|
+
prompt: Parameters<SdkQuery>[0]["prompt"];
|
|
72
|
+
query: SdkQuery;
|
|
73
|
+
- buildOptions: (lane: AnthropicSubscriptionTokenInjection) => Options;
|
|
74
|
+
+ buildOptions: (lane: AnthropicSubscriptionTokenInjection) => Options | Promise<Options>;
|
|
75
|
+
providerSettings: AnthropicSubscriptionProviderSettings;
|
|
76
|
+
/** Effective request auth environment; overrides the host for account discovery and SDK spawn. */
|
|
77
|
+
env?: Record<string, string>;
|
|
78
|
+
@@ -27,4 +27,4 @@ export type AuthenticatedQueryInput = {
|
|
79
|
+
export declare function resolveEffectiveLane(settings: AnthropicSubscriptionProviderSettings, accounts: readonly AccountSlot[]): AnthropicSubscriptionTokenInjection;
|
|
80
|
+
/** Resolves managed OAuth immediately before each subprocess spawn and retries only pre-delta failures. */
|
|
81
|
+
export declare function queryWithAuthLane(input: AuthenticatedQueryInput): AsyncGenerator<SDKMessage>;
|
|
82
|
+
-//# sourceMappingURL=auth-lane.d.ts.map
|
|
83
|
+
|
|
84
|
+
+//# sourceMappingURL=auth-lane.d.ts.map
|
|
85
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js
|
|
86
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js
|
|
87
|
+
@@ -121,7 +121,7 @@ export async function* queryWithAuthLane(input) {
|
|
88
|
+
// Lane-owned variables from buildOptions (the compaction-owner overlay) win over the host.
|
|
89
|
+
let askedLegacyLongContext = false;
|
|
90
|
+
const ambientAttempt = async () => {
|
|
91
|
+
- const options = input.buildOptions("ambient");
|
|
92
|
+
+ const options = await input.buildOptions("ambient");
|
|
93
|
+
askedLegacyLongContext = asksLegacyLongContext(options.model);
|
|
94
|
+
options.env = { ...ambientEnvironment, ...options.env };
|
|
95
|
+
return createAttemptMessages(input, { accountName: "ambient", accounts: [], authLane: "ambient", options });
|
|
96
|
+
@@ -160,7 +160,7 @@ export async function* queryWithAuthLane(input) {
|
|
97
|
+
...(input.model === undefined ? {} : { model: input.model }),
|
|
98
|
+
}),
|
|
99
|
+
runAttempt: async (slot) => {
|
|
100
|
+
- const options = input.buildOptions(pool.lane);
|
|
101
|
+
+ const options = await input.buildOptions(pool.lane);
|
|
102
|
+
askedLegacyLongContext = asksLegacyLongContext(options.model);
|
|
103
|
+
const accounts = pool.accounts.map((account) => ({ ...account }));
|
|
104
|
+
const prepared = await prepareSlot(pool, slot, signal);
|
|
105
|
+
@@ -192,4 +192,4 @@ export async function* queryWithAuthLane(input) {
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
-//# sourceMappingURL=auth-lane.js.map
|
|
110
|
+
|
|
111
|
+
+//# sourceMappingURL=auth-lane.js.map
|
|
112
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/cold-seed-images.js
|
|
113
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/cold-seed-images.js
|
|
114
|
+
@@ -21,6 +21,7 @@ export const coldSeedImageText = {
|
|
115
|
+
toolOutput: (toolName) => `[image returned by the ${toolName} tool to your own call (tool output, not a user attachment)]`,
|
|
116
|
+
userHistory: "[image the user attached in this earlier turn (history, not part of the current message)]",
|
|
117
|
+
duplicate: (origin, first) => `${subject(origin)}: identical to an image already shown above (${source(first)}); not attached again]`,
|
|
118
|
+
+ userAnswered: "[image the user attached in an earlier turn: you already saw and answered it, so it is not attached again and is not part of the current message; ask the user to attach it again if you need it]",
|
|
119
|
+
answered: (origin) => `[image returned earlier by the ${origin.toolName} tool, id=${origin.toolCallId}: already seen earlier in this conversation; run the tool again if you need it]`,
|
|
120
|
+
capped: (origin) => `${subject(origin)}: omitted from this replay, which re-sends at most ${MAX_REPLAYED_HISTORY_IMAGES} earlier images; re-read the source if you need it]`,
|
|
121
|
+
unreadable: (origin) => `${subject(origin)}: omitted because its image data is missing or unreadable]`,
|
|
122
|
+
@@ -86,14 +87,16 @@ function selectUserFirstThenMostRecent(occurrences) {
|
|
123
|
+
return new Set(ordered.slice(0, MAX_REPLAYED_HISTORY_IMAGES).map(([hash]) => hash));
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
- * A tool image the assistant already responded to is not re-sent (#2768): a cold
|
|
127
|
+
- * seed packs the whole history into one user message, and replaying earlier tool
|
|
128
|
+
- * reads there made the model answer plain questions as if the user had just sent
|
|
129
|
+
- * those images. The user's own uploads and images still awaiting a response are
|
|
130
|
+
- * replayed.
|
|
131
|
+
+ * An image the assistant already responded to is not re-sent (#2768): a cold
|
|
132
|
+
+ * seed packs the whole history into one user message, and replaying earlier
|
|
133
|
+
+ * images there made the model answer plain questions as if the user had just
|
|
134
|
+
+ * sent those images. zigmini: this covers the user's own earlier uploads too;
|
|
135
|
+
+ * Claude Code saved each replayed upload as a new images/N.png on every model
|
|
136
|
+
+ * call and the model read it as a new attachment. Images still awaiting a
|
|
137
|
+
+ * response (the current turn) are replayed.
|
|
138
|
+
*/
|
|
139
|
+
function isAnsweredToolImage(occurrence) {
|
|
140
|
+
- return occurrence.answered && occurrence.origin.kind === "tool";
|
|
141
|
+
+ return occurrence.answered;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Rewrites the image entries of a cold-seed history (#2490): a tool-result image
|
|
145
|
+
@@ -124,8 +127,8 @@ export function replayHistoryImages(history, sdkToolName, options = {}) {
|
|
146
|
+
if (decoded === undefined || hash === undefined) {
|
|
147
|
+
replacement = [{ type: "text", text: coldSeedImageText.unreadable(origin) }];
|
|
148
|
+
}
|
|
149
|
+
- else if (origin.kind === "tool" && occurrence.answered && shownEarlier === undefined) {
|
|
150
|
+
- replacement = [{ type: "text", text: coldSeedImageText.answered(origin) }];
|
|
151
|
+
+ else if (occurrence.answered && shownEarlier === undefined) {
|
|
152
|
+
+ replacement = [{ type: "text", text: origin.kind === "tool" ? coldSeedImageText.answered(origin) : coldSeedImageText.userAnswered }];
|
|
153
|
+
}
|
|
154
|
+
else if (!replayed.has(hash) && shownEarlier === undefined) {
|
|
155
|
+
replacement = [{ type: "text", text: coldSeedImageText.capped(origin) }];
|
|
156
|
+
@@ -159,4 +162,4 @@ export function replayHistoryImages(history, sdkToolName, options = {}) {
|
|
157
|
+
}
|
|
158
|
+
return rewritten;
|
|
159
|
+
}
|
|
160
|
+
-//# sourceMappingURL=cold-seed-images.js.map
|
|
161
|
+
|
|
162
|
+
+//# sourceMappingURL=cold-seed-images.js.map
|
|
163
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/errors.js
|
|
164
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/errors.js
|
|
165
|
+
@@ -1,5 +1,6 @@
|
|
166
|
+
import { rateLimitModelFamily } from "../../../credential-pool/model-scope.js";
|
|
167
|
+
import { inheritAccountContext } from "./account-context.js";
|
|
168
|
+
+import { isClaudeSdkRefusal } from "./refusal.js";
|
|
169
|
+
const SDK_ERROR_CLASSIFICATIONS = {
|
|
170
|
+
authentication_failed: { kind: "auth_error", retryable: true },
|
|
171
|
+
oauth_org_not_allowed: { kind: "org_not_allowed", retryable: true },
|
|
172
|
+
@@ -77,6 +78,8 @@ export function sdkAssistantFailure(message) {
|
|
173
|
+
}
|
|
174
|
+
/** Classifies Anthropic Subscription error codes and HTTP-shaped fallback text in one place. */
|
|
175
|
+
export function classifySdkError(error) {
|
|
176
|
+
+ if (isClaudeSdkRefusal(error))
|
|
177
|
+
+ return OTHER_ERROR;
|
|
178
|
+
const classification = classifyErrorKind(error);
|
|
179
|
+
if (classification.kind !== "rate_limit")
|
|
180
|
+
return classification;
|
|
181
|
+
@@ -131,4 +134,4 @@ function classifyErrorKind(error) {
|
|
182
|
+
}
|
|
183
|
+
return OTHER_ERROR;
|
|
184
|
+
}
|
|
185
|
+
-//# sourceMappingURL=errors.js.map
|
|
186
|
+
|
|
187
|
+
+//# sourceMappingURL=errors.js.map
|
|
188
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/refusal.js
|
|
189
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/refusal.js
|
|
190
|
+
@@ -26,6 +26,13 @@ export function refusalError(message) {
|
|
191
|
+
return details ? new ClaudeSdkRefusalError(details.content, details.category) : undefined;
|
|
192
|
+
}
|
|
193
|
+
export function isClaudeSdkRefusal(error) {
|
|
194
|
+
- return error instanceof ClaudeSdkRefusalError;
|
|
195
|
+
+ return getClaudeSdkRefusal(error) !== undefined;
|
|
196
|
+
}
|
|
197
|
+
-//# sourceMappingURL=refusal.js.map
|
|
198
|
+
|
|
199
|
+
+export function getClaudeSdkRefusal(error) {
|
|
200
|
+
+ if (error instanceof ClaudeSdkRefusalError)
|
|
201
|
+
+ return error;
|
|
202
|
+
+ if (error !== null && typeof error === "object" && "original" in error)
|
|
203
|
+
+ return getClaudeSdkRefusal(error.original);
|
|
204
|
+
+ return undefined;
|
|
205
|
+
+}
|
|
206
|
+
+//# sourceMappingURL=refusal.js.map
|
|
207
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js
|
|
208
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js
|
|
209
|
+
@@ -9,7 +9,7 @@ import { buildAnthropicSubscriptionQueryOptions } from "./options.js";
|
|
210
|
+
import { buildDeferredPromptStream, buildPromptBlocks } from "./prompt-bridge.js";
|
|
211
|
+
import { pinOneShotPromptCacheTtl } from "./prompt-cache-ttl.js";
|
|
212
|
+
import { dedupeUltraworkBlocks } from "./prompt-directive-dedupe.js";
|
|
213
|
+
-import { refusalError } from "./refusal.js";
|
|
214
|
+
+import { getClaudeSdkRefusal, refusalError } from "./refusal.js";
|
|
215
|
+
import { getSdkBoundary, loadClaudeAgentSdk } from "./sdk-boundary.js";
|
|
216
|
+
import { emitContinuityObservation } from "./session-observability.js";
|
|
217
|
+
import { forgetBinding } from "./session-reattach.js";
|
|
218
|
+
@@ -76,7 +76,7 @@ export function streamAnthropicSubscription(model, context, options) {
|
|
219
|
+
claudeCodeRun = resolveClaudeCodeRun(defaultExecutableDeps());
|
|
220
|
+
const executable = claudeCodeRun.executable;
|
|
221
|
+
let oneShotCacheTtl;
|
|
222
|
+
- const buildOptions = (authLane) => {
|
|
223
|
+
+ const buildOptions = async (authLane) => {
|
|
224
|
+
const queryOptions = buildAnthropicSubscriptionQueryOptions({
|
|
225
|
+
model,
|
|
226
|
+
context,
|
|
227
|
+
@@ -100,7 +100,8 @@ export function streamAnthropicSubscription(model, context, options) {
|
|
228
|
+
if (!useResidentSession) {
|
|
229
|
+
oneShotCacheTtl = pinOneShotPromptCacheTtl(queryOptions, authLane ?? providerSettings.tokenInjection ?? "ambient", { ...process.env, ...options?.env });
|
|
230
|
+
}
|
|
231
|
+
- return queryOptions;
|
|
232
|
+
+ const replacement = await options?.onPayload?.(queryOptions, model);
|
|
233
|
+
+ return replacement === undefined ? queryOptions : replacement;
|
|
234
|
+
};
|
|
235
|
+
const recordContinuity = (observation) => {
|
|
236
|
+
// Not a failure: carry the observation as details only, with no synthesized error.
|
|
237
|
+
@@ -255,6 +256,9 @@ export function streamAnthropicSubscription(model, context, options) {
|
|
238
|
+
if (nativeCompactionStopped && options?.sessionId)
|
|
239
|
+
forgetBinding(options.sessionId);
|
|
240
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
241
|
+
+ const refusal = getClaudeSdkRefusal(error);
|
|
242
|
+
+ if (refusal)
|
|
243
|
+
+ output.stopDetails = { type: "refusal", explanation: refusal.message };
|
|
244
|
+
// A failed result still bills its tokens; managed and resident lanes
|
|
245
|
+
// throw before the result reaches this loop, so account for it here.
|
|
246
|
+
const billed = sdkResultFailureUsage(error);
|
|
247
|
+
@@ -272,4 +276,4 @@ export function streamAnthropicSubscription(model, context, options) {
|
|
248
|
+
})();
|
|
249
|
+
return stream;
|
|
250
|
+
}
|
|
251
|
+
-//# sourceMappingURL=stream.js.map
|
|
252
|
+
|
|
253
|
+
+//# sourceMappingURL=stream.js.map
|
|
254
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/tps.js
|
|
255
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/tps.js
|
|
256
|
+
@@ -16,6 +16,8 @@ export default function (pi) {
|
|
257
|
+
let lastDeltaMs = null;
|
|
258
|
+
let generationMs = 0;
|
|
259
|
+
let streamedOutput = 0;
|
|
260
|
+
+ let turnId = null;
|
|
261
|
+
+ let completedMessages = [];
|
|
262
|
+
const finishActiveAssistantTiming = () => {
|
|
263
|
+
if (activeAssistantStartMs === null)
|
|
264
|
+
return;
|
|
265
|
+
@@ -34,7 +36,11 @@ export default function (pi) {
|
|
266
|
+
lastDeltaMs = null;
|
|
267
|
+
thinkingStreamed = false;
|
|
268
|
+
};
|
|
269
|
+
- pi.on("agent_start", () => {
|
|
270
|
+
+ pi.on("agent_start", (_event, ctx) => {
|
|
271
|
+
+ turnId = ctx.mode === "rpc" ? crypto.randomUUID() : null;
|
|
272
|
+
+ completedMessages = [];
|
|
273
|
+
+ if (turnId !== null)
|
|
274
|
+
+ pi.rpc.emit("zgw.turn_started", { version: 1, turnId });
|
|
275
|
+
activeAssistantStartMs = null;
|
|
276
|
+
assistantElapsedMs = 0;
|
|
277
|
+
generationMs = 0;
|
|
278
|
+
@@ -69,6 +75,7 @@ export default function (pi) {
|
|
279
|
+
// counted in usage.output may be hidden or only summarised after it finished, so it was generated
|
|
280
|
+
// before the first delta. Such a message (usage.reasoning, or a thinking block whose text never
|
|
281
|
+
// streamed: Bedrock and omitted display report no reasoning count) is timed from its start.
|
|
282
|
+
+ completedMessages.push(event.message);
|
|
283
|
+
const output = event.message.usage?.output ?? 0;
|
|
284
|
+
const windowStart = reasonedBeforeFirstDelta(event.message, thinkingStreamed) ? messageStartMs : firstDeltaMs;
|
|
285
|
+
const counted = event.message.stopReason !== "aborted" && event.message.stopReason !== "error" && output > 0;
|
|
286
|
+
@@ -83,6 +90,43 @@ export default function (pi) {
|
|
287
|
+
const elapsedMs = assistantElapsedMs;
|
|
288
|
+
const measuredGenerationMs = generationMs;
|
|
289
|
+
const measuredOutput = streamedOutput;
|
|
290
|
+
+ // Structured RPC metadata stays out of messages and extension_ui_request notifications.
|
|
291
|
+
+ const completedTurnId = turnId;
|
|
292
|
+
+ turnId = null;
|
|
293
|
+
+ const messages = completedMessages;
|
|
294
|
+
+ completedMessages = [];
|
|
295
|
+
+ const finalMessage = messages.at(-1);
|
|
296
|
+
+ const lastAssistant = event.messages.findLast(isAssistantMessage);
|
|
297
|
+
+ if (ctx.mode === "rpc" && completedTurnId !== null && !event.aborted && !event.willRetry && !event.abortSource && !ctx.signal?.aborted
|
|
298
|
+
+ && finalMessage && (finalMessage.stopReason === "stop" || finalMessage.stopReason === "length")
|
|
299
|
+
+ && Number.isInteger(finalMessage.timestamp) && finalMessage.timestamp > 0
|
|
300
|
+
+ && lastAssistant?.timestamp === finalMessage.timestamp
|
|
301
|
+
+ && !messages.some((message) => message.stopReason === "error" || message.stopReason === "aborted")) {
|
|
302
|
+
+ const stats = {};
|
|
303
|
+
+ const hasOutput = messages.some((message) => Number.isFinite(message.usage?.output) && message.usage.output > 0);
|
|
304
|
+
+ if (hasOutput) {
|
|
305
|
+
+ if (Number.isFinite(elapsedMs) && elapsedMs > 0)
|
|
306
|
+
+ stats.modelDurationMs = elapsedMs;
|
|
307
|
+
+ const rate = measuredOutput / (measuredGenerationMs / 1000);
|
|
308
|
+
+ if (measuredGenerationMs >= MIN_GENERATION_MS && Number.isFinite(rate) && rate > 0)
|
|
309
|
+
+ stats.tokensPerSecond = rate;
|
|
310
|
+
+ // Unknown usage must not look like a measured zero-percent cache hit.
|
|
311
|
+
+ if (messages.every((message) => [message.usage?.input, message.usage?.cacheRead, message.usage?.cacheWrite]
|
|
312
|
+
+ .every((value) => Number.isFinite(value) && value >= 0))) {
|
|
313
|
+
+ let promptTokens = 0;
|
|
314
|
+
+ let cacheRead = 0;
|
|
315
|
+
+ for (const message of messages) {
|
|
316
|
+
+ promptTokens += message.usage.input + message.usage.cacheRead + message.usage.cacheWrite;
|
|
317
|
+
+ cacheRead += message.usage.cacheRead;
|
|
318
|
+
+ }
|
|
319
|
+
+ const hit = cacheRead / promptTokens * 100;
|
|
320
|
+
+ if (Number.isFinite(promptTokens) && Number.isFinite(cacheRead) && Number.isFinite(hit) && hit >= 0 && hit <= 100)
|
|
321
|
+
+ stats.cacheHitPercent = hit;
|
|
322
|
+
+ }
|
|
323
|
+
+ }
|
|
324
|
+
+ if (Object.keys(stats).length > 0)
|
|
325
|
+
+ pi.rpc.emit("zgw.turn_stats", { version: 1, turnId: completedTurnId, finalMessageTimestamp: finalMessage.timestamp, finalMessageIndex: messages.length, stats });
|
|
326
|
+
+ }
|
|
327
|
+
activeAssistantStartMs = null;
|
|
328
|
+
assistantElapsedMs = 0;
|
|
329
|
+
generationMs = 0;
|
|
330
|
+
@@ -121,4 +165,4 @@ export default function (pi) {
|
|
331
|
+
ctx.ui.notify(`${rate}${stats}`, "info");
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
-//# sourceMappingURL=tps.js.map
|
|
335
|
+
|
|
336
|
+
+//# sourceMappingURL=tps.js.map
|
|
337
|
+
--- a/@code-yeongyu/senpi/dist/core/model-runtime.js
|
|
338
|
+
+++ b/@code-yeongyu/senpi/dist/core/model-runtime.js
|
|
339
|
+
@@ -76,6 +76,7 @@ function withPayloadRequestMetadata(options, model) {
|
|
340
|
+
onPayload: async (payload, providerModel) => await onPayload(payload, providerModel, {
|
|
341
|
+
model,
|
|
342
|
+
headers: options.headers ?? {},
|
|
343
|
+
+ streamKind: options.streamKind,
|
|
344
|
+
}),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
@@ -1165,4 +1166,4 @@ export class ModelRuntime {
|
|
348
|
+
return model && !isVirtualModel(model) ? model : undefined;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
-//# sourceMappingURL=model-runtime.js.map
|
|
352
|
+
|
|
353
|
+
+//# sourceMappingURL=model-runtime.js.map
|
|
354
|
+
--- a/@earendil-works/pi-agent-core/dist/agent.d.ts
|
|
355
|
+
+++ b/@earendil-works/pi-agent-core/dist/agent.d.ts
|
|
356
|
+
@@ -194,6 +194,9 @@ export declare class Agent {
|
|
357
|
+
* the continuation input, while timeout overrides still apply to its first provider request.
|
|
358
|
+
*/
|
|
359
|
+
continueWithQueuedMessages(options?: AgentContinuationOptions): Promise<void>;
|
|
360
|
+
+ continueQueuedInput(clientInputId: string, options?: AgentContinuationOptions & {
|
|
361
|
+
+ onStart?: () => void;
|
|
362
|
+
+ }): Promise<"busy" | "not_head" | "resumed">;
|
|
363
|
+
/**
|
|
364
|
+
* Continue from the current transcript. The last message must be a user or tool-result message.
|
|
365
|
+
* Queue deferral and timeout overrides apply only to the first provider request; later requests in
|
|
366
|
+
@@ -242,4 +245,4 @@ export declare class Agent {
|
|
367
|
+
*/
|
|
368
|
+
emitExternalEvent(event: AgentEvent, runSignal?: AbortSignal): Promise<void>;
|
|
369
|
+
}
|
|
370
|
+
-//# sourceMappingURL=agent.d.ts.map
|
|
371
|
+
|
|
372
|
+
+//# sourceMappingURL=agent.d.ts.map
|
|
373
|
+
--- a/@earendil-works/pi-agent-core/dist/agent.js
|
|
374
|
+
+++ b/@earendil-works/pi-agent-core/dist/agent.js
|
|
375
|
+
@@ -302,6 +302,21 @@ export class Agent {
|
|
376
|
+
* Queue-first recovery takes precedence over `deferQueuedMessages`: the selected queued message is
|
|
377
|
+
* the continuation input, while timeout overrides still apply to its first provider request.
|
|
378
|
+
*/
|
|
379
|
+
+ async continueQueuedInput(clientInputId, options = {}) {
|
|
380
|
+
+ if (this.activeRun)
|
|
381
|
+
+ return "busy";
|
|
382
|
+
+ const steering = this.steeringQueue.hasItems();
|
|
383
|
+
+ const queue = steering ? this.steeringQueue : this.followUpQueue;
|
|
384
|
+
+ if (queue.messages[0]?.clientInputId !== clientInputId)
|
|
385
|
+
+ return "not_head";
|
|
386
|
+
+ options.onStart?.();
|
|
387
|
+
+ await this.runQueuedMessages(steering ? "steering" : "followUp", {
|
|
388
|
+
+ skipInitialSteeringPoll: steering,
|
|
389
|
+
+ initialRequestTimeoutMs: options.timeoutMs,
|
|
390
|
+
+ initialRequestStreamStartTimeoutMs: options.streamStartTimeoutMs,
|
|
391
|
+
+ });
|
|
392
|
+
+ return "resumed";
|
|
393
|
+
+ }
|
|
394
|
+
async continueWithQueuedMessages(options = {}) {
|
|
395
|
+
if (this.activeRun) {
|
|
396
|
+
throw new Error("Agent is already processing. Wait for completion before continuing.");
|
|
397
|
+
@@ -681,4 +696,4 @@ export class Agent {
|
|
398
|
+
await this.processEvents(event);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
-//# sourceMappingURL=agent.js.map
|
|
402
|
+
|
|
403
|
+
+//# sourceMappingURL=agent.js.map
|
|
404
|
+
--- a/@earendil-works/pi-ai/dist/types.d.ts
|
|
405
|
+
+++ b/@earendil-works/pi-ai/dist/types.d.ts
|
|
406
|
+
@@ -85,6 +85,7 @@ export type ProviderHeaders = Record<string, string | null>;
|
|
407
|
+
export type ProviderRequestMetadata = {
|
|
408
|
+
model: Model<Api>;
|
|
409
|
+
headers: ProviderHeaders;
|
|
410
|
+
+ streamKind?: "main" | "auxiliary";
|
|
411
|
+
};
|
|
412
|
+
export type FetchFunction = typeof globalThis.fetch;
|
|
413
|
+
export interface ProviderResponse {
|
|
414
|
+
@@ -514,6 +515,8 @@ export interface SystemMessage {
|
|
415
|
+
timestamp: number;
|
|
416
|
+
}
|
|
417
|
+
export interface UserMessage {
|
|
418
|
+
+ /** Gateway input identity preserved through expansion and queue consumption. */
|
|
419
|
+
+ clientInputId?: string;
|
|
420
|
+
role: "user";
|
|
421
|
+
content: string | (TextContent | ImageContent)[];
|
|
422
|
+
timestamp: number;
|
|
423
|
+
@@ -1248,4 +1251,4 @@ export interface ModelTypeMap {
|
|
424
|
+
export type ModelType = keyof ModelTypeMap;
|
|
425
|
+
/** Anything a provider can list. Narrow with `isModelType()`. */
|
|
426
|
+
export type AnyModel = ModelTypeMap[ModelType];
|
|
427
|
+
-//# sourceMappingURL=types.d.ts.map
|
|
428
|
+
|
|
429
|
+
+//# sourceMappingURL=types.d.ts.map
|
|
430
|
+
--- a/omo-ai/bin/lib/package-paths.js
|
|
431
|
+
+++ b/omo-ai/bin/lib/package-paths.js
|
|
432
|
+
@@ -113,7 +113,8 @@ export function resolveSenpi(options = {}) {
|
|
433
|
+
// launcher change.
|
|
434
|
+
const bundlePath = join(distDir, "bundle", "cli.js")
|
|
435
|
+
const unbundledPath = join(distDir, "cli.js")
|
|
436
|
+
- const cliPath = existsSync(bundlePath) ? bundlePath : unbundledPath
|
|
437
|
+
+ // Gateway package ports live in the unbundled graph, including relative RPC workers.
|
|
438
|
+
+ const cliPath = unbundledPath
|
|
439
|
+
if (!existsSync(cliPath)) {
|
|
440
|
+
throw new Error(`senpi CLI is missing at ${cliPath}; reinstall with: ${updateTarget().command}`)
|
|
441
|
+
}
|