@yagni-app/code-staging 1.0.7-staging.1270.1 → 1.0.8-staging.1273.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.md +8 -1
- package/dist/extension/config.d.ts +6 -0
- package/dist/extension/index.d.ts +6 -0
- package/dist/extension/index.js +25 -2
- package/dist/extension/telemetry/config.d.ts +5 -1
- package/dist/extension/telemetry/register.d.ts +7 -0
- package/dist/extension/telemetry/register.js +15 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -203,7 +203,14 @@ prefix instead of `claude_code`:
|
|
|
203
203
|
- **Log events:** `user_prompt`, `assistant_response`, `api_request`,
|
|
204
204
|
`api_error`, `tool_result`, `tool_decision`, `permission_mode_changed`
|
|
205
205
|
(as `event.name`, body `yagni_code.<name>`), with `session.id`,
|
|
206
|
-
`organization.id`, `terminal.type`, and a per-prompt
|
|
206
|
+
`organization.id`, `user.email`, `terminal.type`, and a per-prompt
|
|
207
|
+
`prompt.id`.
|
|
208
|
+
|
|
209
|
+
Every signal carries `organization.id` (the workspace) and `user.email` (the
|
|
210
|
+
signed-in developer's account email, learned from the backend at session
|
|
211
|
+
start) so a collector can attribute sessions and spend per person.
|
|
212
|
+
`OTEL_METRICS_INCLUDE_ACCOUNT_UUID=false` drops the email from all three
|
|
213
|
+
signals. Prompt, response, and tool content never export.
|
|
207
214
|
|
|
208
215
|
Nothing is exported unless you configure an endpoint. Enable it one of three
|
|
209
216
|
ways (first match wins):
|
|
@@ -127,6 +127,12 @@ export interface ContextBrief {
|
|
|
127
127
|
* The opt-in mining beat fires ONLY on an explicit 0.
|
|
128
128
|
*/
|
|
129
129
|
repoDecisionCount?: number;
|
|
130
|
+
/**
|
|
131
|
+
* The signed-in developer's account email (additive; absent on older
|
|
132
|
+
* backends). Feeds the OTel export's `user.email` attribute when the
|
|
133
|
+
* launcher did not already forward one.
|
|
134
|
+
*/
|
|
135
|
+
userEmail?: string;
|
|
130
136
|
}
|
|
131
137
|
/**
|
|
132
138
|
* Attribution headers for the model proxy (YAG-471). On the SERVER side these
|
|
@@ -3,6 +3,7 @@ import { type SpendResponse } from "./costHud.js";
|
|
|
3
3
|
import { runInitPass as defaultRunInitPass } from "./initPass.js";
|
|
4
4
|
import { startMcp as defaultStartMcp } from "./mcp/startup.js";
|
|
5
5
|
import { type GuardianGateEvent } from "./permission/gate.js";
|
|
6
|
+
import { registerTelemetry as defaultRegisterTelemetry } from "./telemetry/register.js";
|
|
6
7
|
import { type PluginCliRunner } from "./plugins/panel.js";
|
|
7
8
|
import { type FlushOutcome, type SpoolClientOpts } from "./spool.js";
|
|
8
9
|
import { type TokenProvider } from "./tokenProvider.js";
|
|
@@ -67,6 +68,11 @@ export interface RegisterYagniDeps {
|
|
|
67
68
|
* fresh-workspace first-run and is skipped otherwise, without a network or disk.
|
|
68
69
|
*/
|
|
69
70
|
runInitPass?: typeof defaultRunInitPass;
|
|
71
|
+
/**
|
|
72
|
+
* OTel export registration. Injectable so tests assert the boot handoff of
|
|
73
|
+
* the /context `userEmail` into the telemetry identity without an SDK.
|
|
74
|
+
*/
|
|
75
|
+
registerTelemetry?: typeof defaultRegisterTelemetry;
|
|
70
76
|
/** The workspace id the token is bound to (keys the one-time init marker). */
|
|
71
77
|
getWorkspaceId?: () => string | undefined;
|
|
72
78
|
/** Has the init pass already run once for this workspace? Injectable (no disk in tests). */
|
package/dist/extension/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
|
|
|
37
37
|
import { DEFAULT_PERMISSION_POLICY, createModeHolder, registerPermissionGate } from "./permission/gate.js";
|
|
38
38
|
import { registerSandbox } from "./sandbox/session.js";
|
|
39
39
|
import { sandboxAutoAllowDecision } from "./sandbox/bash.js";
|
|
40
|
-
import { registerTelemetry } from "./telemetry/register.js";
|
|
40
|
+
import { registerTelemetry as defaultRegisterTelemetry } from "./telemetry/register.js";
|
|
41
41
|
import { loadHooksConfig, makeHookRunner, registerHooks } from "./hooks.js";
|
|
42
42
|
import { loadPermissionRules } from "./permissionRules/loadConfig.js";
|
|
43
43
|
import { registerPluginPanel } from "./plugins/panel.js";
|
|
@@ -421,7 +421,7 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
421
421
|
// OTel export (traces, metrics, log events; Claude Code parity). A no-op
|
|
422
422
|
// unless the launcher's gate set YAGNI_OTEL_EXPORT=1 — see the CLI's
|
|
423
423
|
// otel.ts for the sources and src/telemetry for the exporter.
|
|
424
|
-
const telemetry = registerTelemetry(pi, { env });
|
|
424
|
+
const telemetry = (deps.registerTelemetry ?? defaultRegisterTelemetry)(pi, { env });
|
|
425
425
|
{
|
|
426
426
|
let previousMode = modeHolder.get();
|
|
427
427
|
modeHolder.onSet((m) => {
|
|
@@ -840,6 +840,29 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
840
840
|
contextBrief = undefined;
|
|
841
841
|
}
|
|
842
842
|
}
|
|
843
|
+
// The developer's account email for the OTel export (`user.email`), so a
|
|
844
|
+
// collector can attribute sessions per person. Set before session_start
|
|
845
|
+
// fires, so even the session.count metric carries it. No-op when export is
|
|
846
|
+
// off or the launcher already forwarded YAGNI_USER_EMAIL.
|
|
847
|
+
const bootUserEmail = typeof briefResult?.userEmail === "string" ? briefResult.userEmail : undefined;
|
|
848
|
+
telemetry.setUserEmail(bootUserEmail);
|
|
849
|
+
if (telemetry.enabled && !bootUserEmail && !telemetry.config.identity.userEmail) {
|
|
850
|
+
// Attribution would otherwise degrade silently: leave a trail in the local
|
|
851
|
+
// error sink so "why is user.email missing in the collector" is answerable.
|
|
852
|
+
// Info, not warn: one line per session start, and an account with no
|
|
853
|
+
// email hits it by design. The fields name each way the email can be
|
|
854
|
+
// absent — the boot fetch failed or lacked the field, and whether the
|
|
855
|
+
// launcher's YAGNI_USER_EMAIL was unset or forwarded blank.
|
|
856
|
+
logEvent({
|
|
857
|
+
source: "telemetry",
|
|
858
|
+
level: "info",
|
|
859
|
+
event: "user_email_missing",
|
|
860
|
+
fields: {
|
|
861
|
+
contextFetch: briefResult === null ? "failed_or_skipped" : "no_email_field",
|
|
862
|
+
launcherEmail: env.YAGNI_USER_EMAIL === undefined ? "unset" : "blank",
|
|
863
|
+
},
|
|
864
|
+
});
|
|
865
|
+
}
|
|
843
866
|
// M1 ambient judgment recall: after a `read`, append the recorded judgment for
|
|
844
867
|
// that path to the tool result (result modification is supported on pi's
|
|
845
868
|
// `tool_result` event — verified against 0.80.2). No-op when the corpus is thin
|
|
@@ -37,7 +37,11 @@ export interface SignalConfig {
|
|
|
37
37
|
export interface TelemetryIdentity {
|
|
38
38
|
/** Bound workspace id (YAGNI_WORKSPACE_ID) → `organization.id`. */
|
|
39
39
|
organizationId?: string;
|
|
40
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* The signed-in user's email → `user.email`. From `YAGNI_USER_EMAIL` when
|
|
42
|
+
* the launcher forwards it; otherwise learned from the /context boot fetch
|
|
43
|
+
* (`TelemetryHandle.setUserEmail`). Gated by `includeAccountId`.
|
|
44
|
+
*/
|
|
41
45
|
userEmail?: string;
|
|
42
46
|
/** Shipping CLI version (YAGNI_CODE_VERSION) → `app.version`. */
|
|
43
47
|
appVersion?: string;
|
|
@@ -27,6 +27,13 @@ export interface TelemetryHandle {
|
|
|
27
27
|
toolDecision(input: ToolDecisionInput): void;
|
|
28
28
|
/** The mode holder calls this on every /mode (or shift-tab) change. */
|
|
29
29
|
permissionModeChanged(fromMode: string, toMode: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* The entry calls this with the account email the /context boot fetch
|
|
32
|
+
* returned. A launcher-forwarded `YAGNI_USER_EMAIL` wins; otherwise every
|
|
33
|
+
* span, metric, and event from here on carries `user.email` (still subject
|
|
34
|
+
* to the `includeAccountId` gate).
|
|
35
|
+
*/
|
|
36
|
+
setUserEmail(email: string | undefined): void;
|
|
30
37
|
/** Test/introspection seam: the live tracker once the SDK is up. */
|
|
31
38
|
readonly tracker: SessionTelemetry | null;
|
|
32
39
|
}
|
|
@@ -27,6 +27,7 @@ const NOOP_HANDLE = (config) => ({
|
|
|
27
27
|
config,
|
|
28
28
|
toolDecision: () => { },
|
|
29
29
|
permissionModeChanged: () => { },
|
|
30
|
+
setUserEmail: () => { },
|
|
30
31
|
tracker: null,
|
|
31
32
|
});
|
|
32
33
|
export function registerTelemetry(pi, deps = {}) {
|
|
@@ -187,6 +188,20 @@ export function registerTelemetry(pi, deps = {}) {
|
|
|
187
188
|
},
|
|
188
189
|
toolDecision: guard("tool_decision", (input) => tracker?.toolDecision(input)),
|
|
189
190
|
permissionModeChanged: guard("permission_mode_changed", (from, to) => tracker?.permissionModeChanged(from, to)),
|
|
191
|
+
// Precedence, lowest to highest: this call (the /context boot fetch) <
|
|
192
|
+
// the launcher's YAGNI_USER_EMAIL, which resolveTelemetryConfig already
|
|
193
|
+
// placed on `identity`. So an identity that is set is never overwritten,
|
|
194
|
+
// whichever writer set it; a new writer must slot into this order
|
|
195
|
+
// explicitly rather than rely on call timing. The tracker reads
|
|
196
|
+
// `config.identity` on every attribute set, so this applies whether the
|
|
197
|
+
// SDK is already up or still lazy-loading.
|
|
198
|
+
setUserEmail: (email) => {
|
|
199
|
+
if (config.identity.userEmail)
|
|
200
|
+
return;
|
|
201
|
+
const trimmed = typeof email === "string" ? email.trim() : "";
|
|
202
|
+
if (trimmed)
|
|
203
|
+
config.identity.userEmail = trimmed;
|
|
204
|
+
},
|
|
190
205
|
};
|
|
191
206
|
}
|
|
192
207
|
//# sourceMappingURL=register.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8-staging.1273.1",
|
|
4
4
|
"description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"turndown": "^7.2.4",
|
|
59
59
|
"typebox": "^1.3.15"
|
|
60
60
|
},
|
|
61
|
-
"yagniSourceSha": "
|
|
61
|
+
"yagniSourceSha": "bb2c4a103e2db69e8dcc6eec98c7343ddefca05c"
|
|
62
62
|
}
|