@codemieai/code 0.12.0 → 0.13.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.
- package/README.md +109 -56
- package/bin/codemie-copilot.js +224 -0
- package/bin/codemie-pi.js +18 -0
- package/dist/agents/core/AgentCLI.d.ts.map +1 -1
- package/dist/agents/core/AgentCLI.js +16 -10
- package/dist/agents/core/AgentCLI.js.map +1 -1
- package/dist/agents/core/BaseAgentAdapter.d.ts.map +1 -1
- package/dist/agents/core/BaseAgentAdapter.js +43 -19
- package/dist/agents/core/BaseAgentAdapter.js.map +1 -1
- package/dist/agents/core/agent-aliases.d.ts +17 -0
- package/dist/agents/core/agent-aliases.d.ts.map +1 -0
- package/dist/agents/core/agent-aliases.js +41 -0
- package/dist/agents/core/agent-aliases.js.map +1 -0
- package/dist/agents/core/session/session-origin-audit.d.ts +13 -0
- package/dist/agents/core/session/session-origin-audit.d.ts.map +1 -1
- package/dist/agents/core/session/session-origin-audit.js +34 -0
- package/dist/agents/core/session/session-origin-audit.js.map +1 -1
- package/dist/agents/core/session/stale-session-reconciliation.d.ts +42 -0
- package/dist/agents/core/session/stale-session-reconciliation.d.ts.map +1 -0
- package/dist/agents/core/session/stale-session-reconciliation.js +141 -0
- package/dist/agents/core/session/stale-session-reconciliation.js.map +1 -0
- package/dist/agents/core/session/types.d.ts +19 -0
- package/dist/agents/core/session/types.d.ts.map +1 -1
- package/dist/agents/core/session/types.js +17 -1
- package/dist/agents/core/session/types.js.map +1 -1
- package/dist/agents/core/types.d.ts +1 -0
- package/dist/agents/core/types.d.ts.map +1 -1
- package/dist/agents/plugins/codex/codex.reconciliation.d.ts +15 -35
- package/dist/agents/plugins/codex/codex.reconciliation.d.ts.map +1 -1
- package/dist/agents/plugins/codex/codex.reconciliation.js +8 -139
- package/dist/agents/plugins/codex/codex.reconciliation.js.map +1 -1
- package/dist/agents/plugins/copilot-cli/copilot-cli.constants.d.ts +4 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.constants.d.ts.map +1 -1
- package/dist/agents/plugins/copilot-cli/copilot-cli.constants.js +4 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.constants.js.map +1 -1
- package/dist/agents/plugins/copilot-cli/copilot-cli.models.d.ts +8 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.models.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.models.js +195 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.models.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.plugin.d.ts +4 -20
- package/dist/agents/plugins/copilot-cli/copilot-cli.plugin.d.ts.map +1 -1
- package/dist/agents/plugins/copilot-cli/copilot-cli.plugin.js +237 -38
- package/dist/agents/plugins/copilot-cli/copilot-cli.plugin.js.map +1 -1
- package/dist/agents/plugins/copilot-cli/copilot-cli.session.d.ts.map +1 -1
- package/dist/agents/plugins/copilot-cli/copilot-cli.session.js +14 -2
- package/dist/agents/plugins/copilot-cli/copilot-cli.session.js.map +1 -1
- package/dist/agents/plugins/copilot-cli/index.d.ts +2 -1
- package/dist/agents/plugins/copilot-cli/index.d.ts.map +1 -1
- package/dist/agents/plugins/copilot-cli/index.js +2 -1
- package/dist/agents/plugins/copilot-cli/index.js.map +1 -1
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.conversations-processor.d.ts +6 -3
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.conversations-processor.d.ts.map +1 -1
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.conversations-processor.js +298 -5
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.conversations-processor.js.map +1 -1
- package/dist/agents/plugins/pi/__tests__/home-redirect.d.ts +12 -0
- package/dist/agents/plugins/pi/__tests__/home-redirect.d.ts.map +1 -0
- package/dist/agents/plugins/pi/__tests__/home-redirect.js +27 -0
- package/dist/agents/plugins/pi/__tests__/home-redirect.js.map +1 -0
- package/dist/agents/plugins/pi/extension/README.md +68 -0
- package/dist/agents/plugins/pi/extension/index.js +161 -0
- package/dist/agents/plugins/pi/extension/package.json +8 -0
- package/dist/agents/plugins/pi/index.d.ts +3 -0
- package/dist/agents/plugins/pi/index.d.ts.map +1 -0
- package/dist/agents/plugins/pi/index.js +3 -0
- package/dist/agents/plugins/pi/index.js.map +1 -0
- package/dist/agents/plugins/pi/pi.extension.d.ts +60 -0
- package/dist/agents/plugins/pi/pi.extension.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.extension.js +201 -0
- package/dist/agents/plugins/pi/pi.extension.js.map +1 -0
- package/dist/agents/plugins/pi/pi.incremental-sync.d.ts +63 -0
- package/dist/agents/plugins/pi/pi.incremental-sync.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.incremental-sync.js +266 -0
- package/dist/agents/plugins/pi/pi.incremental-sync.js.map +1 -0
- package/dist/agents/plugins/pi/pi.models.d.ts +20 -0
- package/dist/agents/plugins/pi/pi.models.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.models.js +201 -0
- package/dist/agents/plugins/pi/pi.models.js.map +1 -0
- package/dist/agents/plugins/pi/pi.packages.d.ts +11 -0
- package/dist/agents/plugins/pi/pi.packages.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.packages.js +35 -0
- package/dist/agents/plugins/pi/pi.packages.js.map +1 -0
- package/dist/agents/plugins/pi/pi.paths.d.ts +37 -0
- package/dist/agents/plugins/pi/pi.paths.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.paths.js +146 -0
- package/dist/agents/plugins/pi/pi.paths.js.map +1 -0
- package/dist/agents/plugins/pi/pi.plugin.d.ts +30 -0
- package/dist/agents/plugins/pi/pi.plugin.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.plugin.js +453 -0
- package/dist/agents/plugins/pi/pi.plugin.js.map +1 -0
- package/dist/agents/plugins/pi/pi.session.d.ts +114 -0
- package/dist/agents/plugins/pi/pi.session.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.session.js +388 -0
- package/dist/agents/plugins/pi/pi.session.js.map +1 -0
- package/dist/agents/plugins/pi/pi.setup.d.ts +2 -0
- package/dist/agents/plugins/pi/pi.setup.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.setup.js +33 -0
- package/dist/agents/plugins/pi/pi.setup.js.map +1 -0
- package/dist/agents/plugins/pi/pi.types.d.ts +143 -0
- package/dist/agents/plugins/pi/pi.types.d.ts.map +1 -0
- package/dist/agents/plugins/pi/pi.types.js +74 -0
- package/dist/agents/plugins/pi/pi.types.js.map +1 -0
- package/dist/agents/plugins/pi/session/pi-file-operations.d.ts +17 -0
- package/dist/agents/plugins/pi/session/pi-file-operations.d.ts.map +1 -0
- package/dist/agents/plugins/pi/session/pi-file-operations.js +98 -0
- package/dist/agents/plugins/pi/session/pi-file-operations.js.map +1 -0
- package/dist/agents/plugins/pi/session/pi-named-invocations.d.ts +52 -0
- package/dist/agents/plugins/pi/session/pi-named-invocations.d.ts.map +1 -0
- package/dist/agents/plugins/pi/session/pi-named-invocations.js +189 -0
- package/dist/agents/plugins/pi/session/pi-named-invocations.js.map +1 -0
- package/dist/agents/plugins/pi/session/pi-user-prompt.d.ts +14 -0
- package/dist/agents/plugins/pi/session/pi-user-prompt.d.ts.map +1 -0
- package/dist/agents/plugins/pi/session/pi-user-prompt.js +51 -0
- package/dist/agents/plugins/pi/session/pi-user-prompt.js.map +1 -0
- package/dist/agents/plugins/pi/session/processors/pi.conversations-processor.d.ts +52 -0
- package/dist/agents/plugins/pi/session/processors/pi.conversations-processor.d.ts.map +1 -0
- package/dist/agents/plugins/pi/session/processors/pi.conversations-processor.js +716 -0
- package/dist/agents/plugins/pi/session/processors/pi.conversations-processor.js.map +1 -0
- package/dist/agents/plugins/pi/session/processors/pi.metrics-processor.d.ts +104 -0
- package/dist/agents/plugins/pi/session/processors/pi.metrics-processor.d.ts.map +1 -0
- package/dist/agents/plugins/pi/session/processors/pi.metrics-processor.js +595 -0
- package/dist/agents/plugins/pi/session/processors/pi.metrics-processor.js.map +1 -0
- package/dist/agents/registry.d.ts.map +1 -1
- package/dist/agents/registry.js +2 -2
- package/dist/agents/registry.js.map +1 -1
- package/dist/cli/commands/analytics/agent-labels.d.ts.map +1 -1
- package/dist/cli/commands/analytics/agent-labels.js +1 -0
- package/dist/cli/commands/analytics/agent-labels.js.map +1 -1
- package/dist/cli/commands/analytics/cost/usage-readers.d.ts +24 -0
- package/dist/cli/commands/analytics/cost/usage-readers.d.ts.map +1 -1
- package/dist/cli/commands/analytics/cost/usage-readers.js +199 -54
- package/dist/cli/commands/analytics/cost/usage-readers.js.map +1 -1
- package/dist/cli/commands/analytics/index.js +1 -0
- package/dist/cli/commands/analytics/index.js.map +1 -1
- package/dist/cli/commands/analytics/native-loader.d.ts +21 -4
- package/dist/cli/commands/analytics/native-loader.d.ts.map +1 -1
- package/dist/cli/commands/analytics/native-loader.js +250 -96
- package/dist/cli/commands/analytics/native-loader.js.map +1 -1
- package/dist/cli/commands/analytics/report/client/app.js +47 -3
- package/dist/cli/commands/analytics/report/payload-builder.d.ts.map +1 -1
- package/dist/cli/commands/analytics/report/payload-builder.js +25 -2
- package/dist/cli/commands/analytics/report/payload-builder.js.map +1 -1
- package/dist/cli/commands/analytics/report/types.d.ts +1 -0
- package/dist/cli/commands/analytics/report/types.d.ts.map +1 -1
- package/dist/cli/commands/hook.d.ts.map +1 -1
- package/dist/cli/commands/hook.js +71 -29
- package/dist/cli/commands/hook.js.map +1 -1
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +7 -5
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/commands/list.js +3 -1
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/cli/commands/uninstall.d.ts.map +1 -1
- package/dist/cli/commands/uninstall.js +5 -3
- package/dist/cli/commands/uninstall.js.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +5 -3
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/first-time.d.ts.map +1 -1
- package/dist/cli/first-time.js +7 -6
- package/dist/cli/first-time.js.map +1 -1
- package/dist/providers/core/auth-validation.d.ts +7 -2
- package/dist/providers/core/auth-validation.d.ts.map +1 -1
- package/dist/providers/core/auth-validation.js +11 -5
- package/dist/providers/core/auth-validation.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/claude-request-normalizer.plugin.d.ts +8 -3
- package/dist/providers/plugins/sso/proxy/plugins/claude-request-normalizer.plugin.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/claude-request-normalizer.plugin.js +46 -11
- package/dist/providers/plugins/sso/proxy/plugins/claude-request-normalizer.plugin.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/codex-encrypted-content-sanitizer.plugin.d.ts +23 -13
- package/dist/providers/plugins/sso/proxy/plugins/codex-encrypted-content-sanitizer.plugin.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/codex-encrypted-content-sanitizer.plugin.js +75 -18
- package/dist/providers/plugins/sso/proxy/plugins/codex-encrypted-content-sanitizer.plugin.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/copilot-encrypted-content-sanitizer.plugin.d.ts +21 -0
- package/dist/providers/plugins/sso/proxy/plugins/copilot-encrypted-content-sanitizer.plugin.d.ts.map +1 -0
- package/dist/providers/plugins/sso/proxy/plugins/copilot-encrypted-content-sanitizer.plugin.js +133 -0
- package/dist/providers/plugins/sso/proxy/plugins/copilot-encrypted-content-sanitizer.plugin.js.map +1 -0
- package/dist/providers/plugins/sso/proxy/plugins/header-injection.plugin.js +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/header-injection.plugin.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.d.ts +2 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.js +4 -2
- package/dist/providers/plugins/sso/proxy/plugins/index.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/types.d.ts +11 -0
- package/dist/providers/plugins/sso/proxy/plugins/types.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts +2 -0
- package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.js +37 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.js.map +1 -1
- package/dist/providers/plugins/sso/session/SessionSyncer.d.ts.map +1 -1
- package/dist/providers/plugins/sso/session/SessionSyncer.js +13 -0
- package/dist/providers/plugins/sso/session/SessionSyncer.js.map +1 -1
- package/dist/providers/plugins/sso/session/processors/conversations/syncProcessor.js +6 -0
- package/dist/providers/plugins/sso/session/processors/conversations/syncProcessor.js.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.d.ts +0 -6
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.d.ts.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.js +28 -19
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.js.map +1 -1
- package/dist/utils/interactive.d.ts +14 -0
- package/dist/utils/interactive.d.ts.map +1 -0
- package/dist/utils/interactive.js +16 -0
- package/dist/utils/interactive.js.map +1 -0
- package/package.json +6 -4
- package/scripts/copy-plugins.js +6 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeMie run ledger — a Pi extension.
|
|
3
|
+
*
|
|
4
|
+
* Pi writes its session transcripts lazily, under names it chooses, and a single
|
|
5
|
+
* run can produce several of them (`/new`, `/fork`, `--session` into another
|
|
6
|
+
* project). A wrapper that tries to work out afterwards which files belong to the
|
|
7
|
+
* run it just launched is guessing: transcripts from a concurrent `pi` in the same
|
|
8
|
+
* directory are indistinguishable by id, mtime or lineage. Guessing wrong does not
|
|
9
|
+
* merely lose metrics — it uploads a stranger's prompts and tool output under this
|
|
10
|
+
* user's session.
|
|
11
|
+
*
|
|
12
|
+
* So we do not guess. This extension runs inside Pi, where the answer is a fact:
|
|
13
|
+
* `ctx.sessionManager.getSessionFile()` is the transcript being written right now.
|
|
14
|
+
* It appends that fact, plus every session transition, to a ledger keyed to the
|
|
15
|
+
* CodeMie session id. `onSessionEnd` reads the ledger instead of scanning a
|
|
16
|
+
* directory.
|
|
17
|
+
*
|
|
18
|
+
* ## Invariants — do not relax any of these without reading the note below
|
|
19
|
+
*
|
|
20
|
+
* Pi turns ANY extension load failure into a `type:"error"` diagnostic and then
|
|
21
|
+
* calls `process.exit(1)` (upstream `main.ts:893-899`) — in every mode. A throw in
|
|
22
|
+
* this file's import or factory therefore kills the user's coding session outright.
|
|
23
|
+
* That is a far worse outcome than collecting no metrics, so:
|
|
24
|
+
*
|
|
25
|
+
* 1. Imports are node builtins only. Nothing from `@earendil-works/*`, not even
|
|
26
|
+
* `import type` — a zero-import file removes the question entirely.
|
|
27
|
+
* 2. The whole factory body is wrapped in try/catch.
|
|
28
|
+
* 3. Every handler body is separately wrapped — a throw in `session_shutdown`
|
|
29
|
+
* would truncate the ledger even though Pi catches it (`runner.ts:815-830`).
|
|
30
|
+
* 4. We never subscribe to `tool_call`: a throwing `tool_call` handler *blocks
|
|
31
|
+
* the tool*, which would change what the user's agent can do.
|
|
32
|
+
* 5. Handlers return `undefined`, never a result object, so the `input` handler
|
|
33
|
+
* cannot transform or swallow what the user typed.
|
|
34
|
+
* 6. Only parsed *names* are recorded. Prompt and argument text never enters the
|
|
35
|
+
* ledger.
|
|
36
|
+
*
|
|
37
|
+
* Writes are synchronous (`appendFileSync`) so a hard exit cannot lose the last
|
|
38
|
+
* line, and every write is individually guarded — a read-only disk degrades this
|
|
39
|
+
* to "no metrics", never to a broken session.
|
|
40
|
+
*
|
|
41
|
+
* The ledger path arrives as `CODEMIE_PI_LEDGER` rather than being derived here,
|
|
42
|
+
* so this file holds no assumptions about CodeMie's directory layout. No env var,
|
|
43
|
+
* no ledger, no subscriptions.
|
|
44
|
+
*
|
|
45
|
+
* @see src/agents/plugins/pi/pi.extension.ts — writes this file out and reads it back
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
49
|
+
import { dirname } from 'node:path';
|
|
50
|
+
|
|
51
|
+
const LEDGER_VERSION = 1;
|
|
52
|
+
|
|
53
|
+
/** `/skill:<name>` — Pi's own form, per its interactive skill dispatcher. */
|
|
54
|
+
const SKILL_COMMAND = /^\/skill:([A-Za-z0-9][\w.-]*)/;
|
|
55
|
+
|
|
56
|
+
/** `/<name>` — a prompt template. Anchored and charset-limited so `/usr/local/bin` does not match. */
|
|
57
|
+
const PROMPT_COMMAND = /^\/([A-Za-z][\w.-]*)(?:\s|$)/;
|
|
58
|
+
|
|
59
|
+
/** Call a getter that belongs to Pi, never letting its failure cost us the record. */
|
|
60
|
+
function safe(read) {
|
|
61
|
+
try {
|
|
62
|
+
const value = read();
|
|
63
|
+
return value === undefined ? null : value;
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default function codemieMetrics(pi) {
|
|
70
|
+
try {
|
|
71
|
+
const ledgerPath = process.env.CODEMIE_PI_LEDGER;
|
|
72
|
+
if (!ledgerPath || process.env.CODEMIE_PI_EXTENSION_DISABLED === '1') {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let ledgerDirReady = false;
|
|
77
|
+
const append = (record) => {
|
|
78
|
+
try {
|
|
79
|
+
if (!ledgerDirReady) {
|
|
80
|
+
mkdirSync(dirname(ledgerPath), { recursive: true });
|
|
81
|
+
ledgerDirReady = true;
|
|
82
|
+
}
|
|
83
|
+
appendFileSync(ledgerPath, `${JSON.stringify({ v: LEDGER_VERSION, ts: Date.now(), ...record })}\n`);
|
|
84
|
+
} catch {
|
|
85
|
+
// A ledger we cannot write is a metrics problem, not a session problem.
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* `getCwd()` is the transcript's own cwd, which is what we want: `--session
|
|
91
|
+
* <path>` runs the whole session under the cwd recorded in that file's header,
|
|
92
|
+
* not the directory the wrapper was launched from.
|
|
93
|
+
*/
|
|
94
|
+
const sessionFacts = (ctx) => ({
|
|
95
|
+
file: safe(() => ctx.sessionManager.getSessionFile()),
|
|
96
|
+
piSessionId: safe(() => ctx.sessionManager.getSessionId()),
|
|
97
|
+
cwd: safe(() => ctx.sessionManager.getCwd()),
|
|
98
|
+
mode: safe(() => ctx.mode),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
append({ t: 'boot', pid: process.pid });
|
|
102
|
+
|
|
103
|
+
pi.on('session_start', (event, ctx) => {
|
|
104
|
+
try {
|
|
105
|
+
append({
|
|
106
|
+
t: 'session',
|
|
107
|
+
reason: safe(() => event.reason),
|
|
108
|
+
prevFile: safe(() => event.previousSessionFile),
|
|
109
|
+
...sessionFacts(ctx),
|
|
110
|
+
});
|
|
111
|
+
} catch {
|
|
112
|
+
// never propagate
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
pi.on('session_shutdown', (event, ctx) => {
|
|
118
|
+
try {
|
|
119
|
+
append({
|
|
120
|
+
t: 'shutdown',
|
|
121
|
+
reason: safe(() => event.reason),
|
|
122
|
+
target: safe(() => event.targetSessionFile),
|
|
123
|
+
...sessionFacts(ctx),
|
|
124
|
+
});
|
|
125
|
+
} catch {
|
|
126
|
+
// never propagate
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Slash commands are the one signal the transcript cannot carry: Pi expands a
|
|
133
|
+
* prompt template into plain user text before persisting it, so `/review` is
|
|
134
|
+
* gone by the time the entry is written. `input` fires with the raw text.
|
|
135
|
+
*
|
|
136
|
+
* Only the command name is recorded — never its arguments.
|
|
137
|
+
*/
|
|
138
|
+
pi.on('input', (event) => {
|
|
139
|
+
try {
|
|
140
|
+
const text = safe(() => event.text);
|
|
141
|
+
if (typeof text !== 'string' || text.charCodeAt(0) !== 47 /* '/' */) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
const skill = SKILL_COMMAND.exec(text);
|
|
145
|
+
if (skill) {
|
|
146
|
+
append({ t: 'cmd', kind: 'skill', name: skill[1] });
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const prompt = PROMPT_COMMAND.exec(text);
|
|
150
|
+
if (prompt) {
|
|
151
|
+
append({ t: 'cmd', kind: 'prompt', name: prompt[1] });
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// never propagate
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
});
|
|
158
|
+
} catch {
|
|
159
|
+
// A throw here would make Pi exit(1) and take the user's session with it.
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/agents/plugins/pi/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/agents/plugins/pi/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materializes the CodeMie run-ledger extension into Pi's agent directory, and
|
|
3
|
+
* reads back the ledger it produces.
|
|
4
|
+
*
|
|
5
|
+
* The ledger replaces post-hoc transcript attribution: instead of scanning Pi's
|
|
6
|
+
* session directory afterwards and inferring which files this run wrote, the
|
|
7
|
+
* extension records each transcript from inside Pi as it is opened. See
|
|
8
|
+
* `extension/README.md` for why, and for the constraints the asset operates under.
|
|
9
|
+
*/
|
|
10
|
+
/** Absolute ledger path, handed to the extension so it holds no layout assumptions. */
|
|
11
|
+
export declare const LEDGER_PATH_ENV = "CODEMIE_PI_LEDGER";
|
|
12
|
+
export interface PiExtensionStatus {
|
|
13
|
+
installed: boolean;
|
|
14
|
+
path?: string;
|
|
15
|
+
reason?: 'disabled' | 'source-missing' | 'selftest-failed' | 'write-failed';
|
|
16
|
+
}
|
|
17
|
+
export interface PiRunLedger {
|
|
18
|
+
/** True when the extension ran at all. False means the run produced no attributable data. */
|
|
19
|
+
loaded: boolean;
|
|
20
|
+
/** Transcripts this run wrote, deduped, existence-filtered, in first-seen order. */
|
|
21
|
+
transcripts: string[];
|
|
22
|
+
/** The last transcript the run was writing — the one to correlate the session record to. */
|
|
23
|
+
primaryTranscript?: string;
|
|
24
|
+
/** Pi's own session id for `primaryTranscript`. */
|
|
25
|
+
piSessionId?: string;
|
|
26
|
+
/** The transcript's cwd, which `--session <path>` makes differ from the launch cwd. */
|
|
27
|
+
cwd?: string;
|
|
28
|
+
/** `/name` prompt-template invocations, which never reach the transcript. */
|
|
29
|
+
commandInvocations: Record<string, number>;
|
|
30
|
+
/** `/skill:name` invocations, corroborating the transcript's `<skill>` wrapper. */
|
|
31
|
+
skillCommandInvocations: Record<string, number>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* `~/.codemie/sessions/{sessionId}_pi-run.jsonl`, beside the metrics and conversation
|
|
35
|
+
* JSONL files. The `.jsonl` suffix keeps it clear of every consumer that enumerates
|
|
36
|
+
* session records, all of which filter on `.json`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getPiRunLedgerPath(sessionId: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Copy the extension into Pi's agent dir, then prove it loads.
|
|
41
|
+
*
|
|
42
|
+
* Pi turns any extension load failure into `process.exit(1)`, so a corrupted or
|
|
43
|
+
* truncated asset would kill the user's session rather than merely lose metrics.
|
|
44
|
+
* The pre-flight import below is the same check Pi performs (`typeof factory ===
|
|
45
|
+
* 'function'`); failing it here deletes the copy, so Pi never sees it.
|
|
46
|
+
*
|
|
47
|
+
* Never throws: every failure degrades to `{installed: false}`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function ensurePiCodeMieExtension(cwd?: string, env?: NodeJS.ProcessEnv, sourceDir?: string): Promise<PiExtensionStatus>;
|
|
50
|
+
/**
|
|
51
|
+
* Read the ledger the extension wrote during the run.
|
|
52
|
+
*
|
|
53
|
+
* Malformed lines are skipped individually rather than failing the read — a
|
|
54
|
+
* truncated final line after a hard kill must not discard the records before it.
|
|
55
|
+
* Transcript paths are existence-filtered because `getSessionFile()` reports the
|
|
56
|
+
* path Pi *plans* to write, and Pi does not create the file until the session holds
|
|
57
|
+
* an assistant message.
|
|
58
|
+
*/
|
|
59
|
+
export declare function readPiRunLedger(sessionId: string): Promise<PiRunLedger>;
|
|
60
|
+
//# sourceMappingURL=pi.extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.extension.d.ts","sourceRoot":"","sources":["../../../../src/agents/plugins/pi/pi.extension.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiBH,uFAAuF;AACvF,eAAO,MAAM,eAAe,sBAAsB,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,UAAU,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,cAAc,CAAC;CAC7E;AAED,MAAM,WAAW,WAAW;IAC1B,6FAA6F;IAC7F,MAAM,EAAE,OAAO,CAAC;IAChB,oFAAoF;IACpF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,4FAA4F;IAC5F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,mFAAmF;IACnF,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5D;AAMD;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,GAAG,GAAE,MAAsB,EAC3B,GAAG,GAAE,MAAM,CAAC,UAAwB,EAGpC,SAAS,GAAE,MAAuD,GACjE,OAAO,CAAC,iBAAiB,CAAC,CA0C5B;AAkDD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAiE7E"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materializes the CodeMie run-ledger extension into Pi's agent directory, and
|
|
3
|
+
* reads back the ledger it produces.
|
|
4
|
+
*
|
|
5
|
+
* The ledger replaces post-hoc transcript attribution: instead of scanning Pi's
|
|
6
|
+
* session directory afterwards and inferring which files this run wrote, the
|
|
7
|
+
* extension records each transcript from inside Pi as it is opened. See
|
|
8
|
+
* `extension/README.md` for why, and for the constraints the asset operates under.
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from 'crypto';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import { mkdir, readFile, readdir, rm, writeFile } from 'fs/promises';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { pathToFileURL } from 'url';
|
|
15
|
+
import { logger } from '../../../utils/logger.js';
|
|
16
|
+
import { getCodemiePath, getDirname } from '../../../utils/paths.js';
|
|
17
|
+
import { getPiAgentDir } from './pi.paths.js';
|
|
18
|
+
/** Directory name under `<piAgentDir>/extensions/` that holds the materialized asset. */
|
|
19
|
+
const EXTENSION_DIR_NAME = 'codemie-metrics';
|
|
20
|
+
/** Set to `1` to skip installation entirely and remove any previously installed copy. */
|
|
21
|
+
const DISABLE_ENV = 'CODEMIE_PI_EXTENSION_DISABLED';
|
|
22
|
+
/** Absolute ledger path, handed to the extension so it holds no layout assumptions. */
|
|
23
|
+
export const LEDGER_PATH_ENV = 'CODEMIE_PI_LEDGER';
|
|
24
|
+
/**
|
|
25
|
+
* `~/.codemie/sessions/{sessionId}_pi-run.jsonl`, beside the metrics and conversation
|
|
26
|
+
* JSONL files. The `.jsonl` suffix keeps it clear of every consumer that enumerates
|
|
27
|
+
* session records, all of which filter on `.json`.
|
|
28
|
+
*/
|
|
29
|
+
export function getPiRunLedgerPath(sessionId) {
|
|
30
|
+
return getCodemiePath('sessions', `${sessionId}_pi-run.jsonl`);
|
|
31
|
+
}
|
|
32
|
+
function emptyLedger() {
|
|
33
|
+
return { loaded: false, transcripts: [], commandInvocations: {}, skillCommandInvocations: {} };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Copy the extension into Pi's agent dir, then prove it loads.
|
|
37
|
+
*
|
|
38
|
+
* Pi turns any extension load failure into `process.exit(1)`, so a corrupted or
|
|
39
|
+
* truncated asset would kill the user's session rather than merely lose metrics.
|
|
40
|
+
* The pre-flight import below is the same check Pi performs (`typeof factory ===
|
|
41
|
+
* 'function'`); failing it here deletes the copy, so Pi never sees it.
|
|
42
|
+
*
|
|
43
|
+
* Never throws: every failure degrades to `{installed: false}`.
|
|
44
|
+
*/
|
|
45
|
+
export async function ensurePiCodeMieExtension(cwd = process.cwd(), env = process.env,
|
|
46
|
+
// Defaulted rather than resolved inline so the self-test failure path — the one
|
|
47
|
+
// that stands between a corrupted asset and `pi` exiting 1 — is reachable in tests.
|
|
48
|
+
sourceDir = join(getDirname(import.meta.url), 'extension')) {
|
|
49
|
+
const targetDir = join(getPiAgentDir(cwd), 'extensions', EXTENSION_DIR_NAME);
|
|
50
|
+
if (env[DISABLE_ENV] === '1') {
|
|
51
|
+
await removeExtension(targetDir);
|
|
52
|
+
return { installed: false, reason: 'disabled' };
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const files = await readSourceFiles(sourceDir);
|
|
56
|
+
if (!files) {
|
|
57
|
+
logger.debug(`[pi-extension] Source assets not found at ${sourceDir}; run ledger disabled`);
|
|
58
|
+
return { installed: false, reason: 'source-missing' };
|
|
59
|
+
}
|
|
60
|
+
await mkdir(targetDir, { recursive: true });
|
|
61
|
+
for (const [name, content] of Object.entries(files)) {
|
|
62
|
+
const targetPath = join(targetDir, name);
|
|
63
|
+
// Rewrite only on change: an untouched mtime keeps Pi's `/reload` quiet and
|
|
64
|
+
// avoids churn in a directory the user may be watching.
|
|
65
|
+
if (await fileMatches(targetPath, content))
|
|
66
|
+
continue;
|
|
67
|
+
await writeFile(targetPath, content, 'utf-8');
|
|
68
|
+
}
|
|
69
|
+
const entryPath = join(targetDir, 'index.js');
|
|
70
|
+
if (!(await exportsFactory(entryPath, files['index.js']))) {
|
|
71
|
+
await removeExtension(targetDir);
|
|
72
|
+
logger.warn('[pi-extension] Run-ledger extension failed its load self-test and was removed. '
|
|
73
|
+
+ 'Pi will start normally; this run will report no tool metrics.');
|
|
74
|
+
return { installed: false, reason: 'selftest-failed' };
|
|
75
|
+
}
|
|
76
|
+
logger.debug(`[pi-extension] Run ledger installed at ${targetDir}`);
|
|
77
|
+
return { installed: true, path: targetDir };
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
logger.warn(`[pi-extension] Could not install the run-ledger extension: ${message}`);
|
|
82
|
+
await removeExtension(targetDir);
|
|
83
|
+
return { installed: false, reason: 'write-failed' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Read every asset the extension directory ships. Returns undefined when it is absent. */
|
|
87
|
+
async function readSourceFiles(sourceDir) {
|
|
88
|
+
if (!existsSync(sourceDir))
|
|
89
|
+
return undefined;
|
|
90
|
+
const files = {};
|
|
91
|
+
for (const name of await readdir(sourceDir)) {
|
|
92
|
+
// README.md is documentation for maintainers, not part of what Pi loads.
|
|
93
|
+
if (name !== 'index.js' && name !== 'package.json')
|
|
94
|
+
continue;
|
|
95
|
+
files[name] = await readFile(join(sourceDir, name), 'utf-8');
|
|
96
|
+
}
|
|
97
|
+
return files['index.js'] && files['package.json'] ? files : undefined;
|
|
98
|
+
}
|
|
99
|
+
async function fileMatches(path, content) {
|
|
100
|
+
try {
|
|
101
|
+
return (await readFile(path, 'utf-8')) === content;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Import the materialized copy and check it exports a factory function, matching
|
|
109
|
+
* Pi's own acceptance test. The content hash busts Node's module cache so a
|
|
110
|
+
* rewritten asset is re-checked rather than resolved to the previous version.
|
|
111
|
+
*/
|
|
112
|
+
async function exportsFactory(entryPath, content) {
|
|
113
|
+
try {
|
|
114
|
+
const cacheKey = createHash('sha1').update(content).digest('hex').slice(0, 12);
|
|
115
|
+
const module = (await import(`${pathToFileURL(entryPath).href}?v=${cacheKey}`));
|
|
116
|
+
return typeof module.default === 'function';
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
logger.debug(`[pi-extension] Self-test import failed: ${message}`);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function removeExtension(targetDir) {
|
|
125
|
+
try {
|
|
126
|
+
await rm(targetDir, { recursive: true, force: true });
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Leaving a stale copy is survivable; failing the run is not.
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Read the ledger the extension wrote during the run.
|
|
134
|
+
*
|
|
135
|
+
* Malformed lines are skipped individually rather than failing the read — a
|
|
136
|
+
* truncated final line after a hard kill must not discard the records before it.
|
|
137
|
+
* Transcript paths are existence-filtered because `getSessionFile()` reports the
|
|
138
|
+
* path Pi *plans* to write, and Pi does not create the file until the session holds
|
|
139
|
+
* an assistant message.
|
|
140
|
+
*/
|
|
141
|
+
export async function readPiRunLedger(sessionId) {
|
|
142
|
+
const ledger = emptyLedger();
|
|
143
|
+
const ledgerPath = getPiRunLedgerPath(sessionId);
|
|
144
|
+
let raw;
|
|
145
|
+
try {
|
|
146
|
+
raw = await readFile(ledgerPath, 'utf-8');
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
logger.debug(`[pi-extension] No run ledger at ${ledgerPath}`);
|
|
150
|
+
return ledger;
|
|
151
|
+
}
|
|
152
|
+
const seen = new Set();
|
|
153
|
+
for (const line of raw.split('\n')) {
|
|
154
|
+
const trimmed = line.trim();
|
|
155
|
+
if (!trimmed)
|
|
156
|
+
continue;
|
|
157
|
+
let record;
|
|
158
|
+
try {
|
|
159
|
+
record = JSON.parse(trimmed);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
switch (record.t) {
|
|
165
|
+
case 'boot':
|
|
166
|
+
ledger.loaded = true;
|
|
167
|
+
break;
|
|
168
|
+
case 'session':
|
|
169
|
+
case 'shutdown': {
|
|
170
|
+
// The last record wins for cwd and identity: a run that forked or switched
|
|
171
|
+
// sessions ends attached to the file it was writing when it stopped.
|
|
172
|
+
if (typeof record.cwd === 'string' && record.cwd)
|
|
173
|
+
ledger.cwd = record.cwd;
|
|
174
|
+
const file = record.file;
|
|
175
|
+
if (typeof file !== 'string' || !file || !existsSync(file))
|
|
176
|
+
break;
|
|
177
|
+
if (!seen.has(file)) {
|
|
178
|
+
seen.add(file);
|
|
179
|
+
ledger.transcripts.push(file);
|
|
180
|
+
}
|
|
181
|
+
ledger.primaryTranscript = file;
|
|
182
|
+
ledger.piSessionId = typeof record.piSessionId === 'string' ? record.piSessionId : undefined;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
case 'cmd': {
|
|
186
|
+
const name = record.name;
|
|
187
|
+
if (typeof name !== 'string' || !name)
|
|
188
|
+
break;
|
|
189
|
+
const target = record.kind === 'skill' ? ledger.skillCommandInvocations : ledger.commandInvocations;
|
|
190
|
+
target[name] = (target[name] ?? 0) + 1;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
default:
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
logger.debug(`[pi-extension] Ledger for ${sessionId}: loaded=${ledger.loaded}, `
|
|
198
|
+
+ `${ledger.transcripts.length} transcript(s), cwd=${ledger.cwd ?? 'unset'}`);
|
|
199
|
+
return ledger;
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=pi.extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.extension.js","sourceRoot":"","sources":["../../../../src/agents/plugins/pi/pi.extension.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi Incremental Sync Timer
|
|
3
|
+
*
|
|
4
|
+
* Pi has no hook surface that fires mid-run, so without this timer a session's
|
|
5
|
+
* metrics exist only in memory until `onSessionEnd` flushes them. A SIGKILL, a
|
|
6
|
+
* closed terminal, or an OS shutdown therefore loses the entire run. Ticking every
|
|
7
|
+
* ~30 s writes per-record metric deltas to JSONL and uploads whatever is PENDING,
|
|
8
|
+
* so at most one interval's worth of work is ever at risk.
|
|
9
|
+
*
|
|
10
|
+
* Which transcripts to process is read from the run ledger the CodeMie extension
|
|
11
|
+
* writes from inside Pi — never inferred from the session directory. A directory
|
|
12
|
+
* listing cannot tell this run's transcripts from a concurrent `pi`'s, and claiming
|
|
13
|
+
* one uploads a stranger's prompts under this user's identity.
|
|
14
|
+
*
|
|
15
|
+
* @see pi.extension.ts
|
|
16
|
+
*/
|
|
17
|
+
import type { ProcessingContext } from '../../../agents/core/session/BaseProcessor.js';
|
|
18
|
+
import type { SessionAdapter } from '../../../agents/core/session/BaseSessionAdapter.js';
|
|
19
|
+
import type { AgentMetadata } from '../../../agents/core/types.js';
|
|
20
|
+
export interface StartPiIncrementalSyncOptions {
|
|
21
|
+
/** CodeMie session id — names both the ledger and the metric JSONL files. */
|
|
22
|
+
sessionId: string;
|
|
23
|
+
/** Pi agent metadata, used to build the default session adapter. */
|
|
24
|
+
metadata: AgentMetadata;
|
|
25
|
+
/** Builds a fresh ProcessingContext on each tick (cookies/version may rotate). */
|
|
26
|
+
buildContext: () => ProcessingContext;
|
|
27
|
+
/** CodeMie SSO URL used to load stored credentials (e.g. env.CODEMIE_URL). */
|
|
28
|
+
ssoUrl?: string;
|
|
29
|
+
/** Sync API base URL for the upload context (env.CODEMIE_SYNC_API_URL ?? env.CODEMIE_BASE_URL). */
|
|
30
|
+
syncApiUrl?: string;
|
|
31
|
+
/** CLI version string forwarded to the upload context. */
|
|
32
|
+
cliVersion?: string;
|
|
33
|
+
/** Overrides the default and the env interval. Injected by tests. */
|
|
34
|
+
intervalMs?: number;
|
|
35
|
+
/** Overrides the default `PiSessionAdapter`. Injected by tests. */
|
|
36
|
+
adapter?: SessionAdapter;
|
|
37
|
+
}
|
|
38
|
+
export declare function startPiIncrementalSync(options: StartPiIncrementalSyncOptions): void;
|
|
39
|
+
/**
|
|
40
|
+
* Stop ticking and wait — for a bounded time — on a tick that is already running.
|
|
41
|
+
*
|
|
42
|
+
* The wait is the point. `clearInterval` cancels the *schedule*, not the tick already
|
|
43
|
+
* executing, and the caller's next move is the end-of-session flush over the same
|
|
44
|
+
* `{sessionId}_metrics.jsonl`. Both read the file's record ids before either appends, so
|
|
45
|
+
* an overlap appends the same deltas twice — and the sync path uploads pending records
|
|
46
|
+
* without deduplicating them by record id.
|
|
47
|
+
*
|
|
48
|
+
* The bound is the point too. This runs on the way out of the CLI, and the tick's upload
|
|
49
|
+
* is a 30 s API timeout over three attempts *per batch*: waiting it out would hang the
|
|
50
|
+
* exit for minutes whenever the sync host black-holes traffic. So the tick is first told
|
|
51
|
+
* to skip its upload — the phase that can take minutes and that appends nothing — and only
|
|
52
|
+
* the remainder is awaited, under {@link STOP_DRAIN_TIMEOUT_MS}.
|
|
53
|
+
*
|
|
54
|
+
* If the bound expires the tick was already mid-upload, and the hazard accepted is the
|
|
55
|
+
* inverse of the double-append: `MetricsSyncProcessor` finishes by rewriting the whole
|
|
56
|
+
* metrics file from the snapshot it read, so deltas the session-end flush appends in the
|
|
57
|
+
* meantime can be dropped. One truncated shutdown is the price of never hanging every
|
|
58
|
+
* user whose network is down.
|
|
59
|
+
*/
|
|
60
|
+
export declare function stopPiIncrementalSync(sessionId: string,
|
|
61
|
+
/** Overrides {@link STOP_DRAIN_TIMEOUT_MS}. Injected by tests. */
|
|
62
|
+
drainTimeoutMs?: number): Promise<void>;
|
|
63
|
+
//# sourceMappingURL=pi.incremental-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.incremental-sync.d.ts","sourceRoot":"","sources":["../../../../src/agents/plugins/pi/pi.incremental-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAM5D,MAAM,WAAW,6BAA6B;IAC5C,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,QAAQ,EAAE,aAAa,CAAC;IACxB,kFAAkF;IAClF,YAAY,EAAE,MAAM,iBAAiB,CAAC;IACtC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mGAAmG;IACnG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AA2BD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,IAAI,CAyBnF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM;AACjB,kEAAkE;AAClE,cAAc,GAAE,MAA8B,GAC7C,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|