gipity 1.1.4 → 1.1.6
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/dist/adopt-cwd.js +1 -1
- package/dist/agents/agy.js +52 -0
- package/dist/agents/index.js +2 -0
- package/dist/api.js +19 -3
- package/dist/banner.js +3 -5
- package/dist/capture/sources/agy.js +88 -0
- package/dist/client-context.js +9 -0
- package/dist/commands/add.js +1 -1
- package/dist/commands/brand.js +125 -0
- package/dist/commands/build.js +13 -12
- package/dist/commands/chat.js +1 -1
- package/dist/commands/deploy.js +1 -1
- package/dist/commands/fn.js +2 -8
- package/dist/commands/generate.js +5 -5
- package/dist/commands/gmail.js +1 -1
- package/dist/commands/init.js +2 -2
- package/dist/commands/job.js +10 -5
- package/dist/commands/key.js +91 -0
- package/dist/commands/load.js +2 -2
- package/dist/commands/login.js +7 -2
- package/dist/commands/page-eval.js +47 -8
- package/dist/commands/page-fetch.js +14 -10
- package/dist/commands/page-inspect.js +2 -2
- package/dist/commands/page-screenshot.js +5 -5
- package/dist/commands/page-test.js +1 -1
- package/dist/commands/records.js +57 -19
- package/dist/commands/remove.js +1 -1
- package/dist/commands/sandbox.js +1 -1
- package/dist/commands/save.js +1 -1
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/service.js +1 -1
- package/dist/commands/setup.js +4 -6
- package/dist/commands/status.js +41 -12
- package/dist/commands/storage.js +2 -2
- package/dist/commands/sync.js +15 -0
- package/dist/commands/test.js +1 -1
- package/dist/commands/token.js +6 -1
- package/dist/commands/uninstall.js +14 -1
- package/dist/commands/upload.js +1 -1
- package/dist/commands/workflow.js +1 -1
- package/dist/hooks/capture-runner.js +20 -8
- package/dist/index.js +1046 -518
- package/dist/knowledge.js +4 -1
- package/dist/login-flow.js +44 -2
- package/dist/project-setup.js +2 -0
- package/dist/relay/diagnostics.js +4 -2
- package/dist/relay/onboarding.js +26 -44
- package/dist/setup.js +198 -16
- package/dist/sync.js +6 -6
- package/dist/template-vars.js +74 -0
- package/dist/utils.js +60 -3
- package/package.json +2 -2
package/dist/commands/status.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join, resolve } from 'path';
|
|
|
4
4
|
import { homedir } from 'os';
|
|
5
5
|
import { getAuth, sessionExpired } from '../auth.js';
|
|
6
6
|
import { get, usingEnvToken, ApiError } from '../api.js';
|
|
7
|
-
import { getConfig, liveUrl } from '../config.js';
|
|
7
|
+
import { getConfig, liveUrl, resolveApiBase } from '../config.js';
|
|
8
8
|
import { brand, success, warning, muted, error as clrError } from '../colors.js';
|
|
9
9
|
import { GIPITY_PLUGIN_ID, GIPITY_MARKETPLACE_NAME, setupClaudeHooks, ensureGipityPlugin, ensureGipityPluginInstalled, userScopeInstallState } from '../setup.js';
|
|
10
10
|
import { flushBugQueue } from '../bug-queue.js';
|
|
@@ -39,15 +39,20 @@ function checkGipityPlugin() {
|
|
|
39
39
|
const stale = missing.length === 1 && missing[0] === 'install' && install.exists;
|
|
40
40
|
return { missing, ok: missing.length === 0, stale };
|
|
41
41
|
}
|
|
42
|
+
/** `account` is the server's `account_slug` for the authenticated identity
|
|
43
|
+
* (only populated on 'ok' - the /users/me call that proved the probe also
|
|
44
|
+
* returns it) - used for the ownership cross-check against the locally
|
|
45
|
+
* cached project (bug cli#S2: a wrong-account session used to read as
|
|
46
|
+
* fully healthy until a later command 404'd). */
|
|
42
47
|
async function probeAuth(loggedIn) {
|
|
43
48
|
if (!loggedIn && !usingEnvToken())
|
|
44
|
-
return 'none';
|
|
49
|
+
return { state: 'none', account: null };
|
|
45
50
|
if (!usingEnvToken() && sessionExpired())
|
|
46
|
-
return 'expired';
|
|
51
|
+
return { state: 'expired', account: null };
|
|
47
52
|
// Cap the probe well below the API layer's 60s request timeout - status is
|
|
48
53
|
// a diagnostic command and must answer fast even when the network is dark.
|
|
49
|
-
const timeout = new Promise(res => setTimeout(() => res('unreachable'), 5000).unref?.());
|
|
50
|
-
const call = get('/users/me').then(() => 'ok', (err) => (err instanceof ApiError && err.statusCode === 401 ? 'rejected' : 'unreachable'));
|
|
54
|
+
const timeout = new Promise(res => setTimeout(() => res({ state: 'unreachable', account: null }), 5000).unref?.());
|
|
55
|
+
const call = get('/users/me').then((res) => ({ state: 'ok', account: res.data?.accountSlug ?? null }), (err) => ({ state: (err instanceof ApiError && err.statusCode === 401 ? 'rejected' : 'unreachable'), account: null }));
|
|
51
56
|
return Promise.race([call, timeout]);
|
|
52
57
|
}
|
|
53
58
|
// `whoami` is the name agents reach for first when they want the signed-in
|
|
@@ -71,6 +76,12 @@ export const statusCommand = new Command('status')
|
|
|
71
76
|
// is empty, which is the common case, so this stays a no-op most runs.
|
|
72
77
|
const queueDelivered = (auth && !sessionExpired()) ? await flushBugQueue().catch(() => 0) : 0;
|
|
73
78
|
const probe = await probeAuth(!!auth);
|
|
79
|
+
// RBAC lets a project be shared to a collaborator whose own account
|
|
80
|
+
// legitimately differs from the project owner's - so this is advisory,
|
|
81
|
+
// never a hard error. Only meaningful once a live 'ok' call has actually
|
|
82
|
+
// returned an account to compare against config.accountSlug.
|
|
83
|
+
const accountMismatch = probe.state === 'ok' && !!config && !!probe.account && probe.account !== config.accountSlug;
|
|
84
|
+
const apiBaseInUse = resolveApiBase();
|
|
74
85
|
if (opts.json) {
|
|
75
86
|
console.log(JSON.stringify({
|
|
76
87
|
project: config ? {
|
|
@@ -78,17 +89,21 @@ export const statusCommand = new Command('status')
|
|
|
78
89
|
slug: config.projectSlug,
|
|
79
90
|
account: config.accountSlug,
|
|
80
91
|
apiBase: config.apiBase,
|
|
92
|
+
apiBaseInUse,
|
|
81
93
|
url: liveUrl(config),
|
|
82
94
|
} : null,
|
|
83
95
|
// `valid` reflects the refresh token (the real session) - access
|
|
84
96
|
// tokens auto-renew, so their expiry must not read as "invalid".
|
|
85
97
|
// `probe` is what one live call just proved: 'rejected' means every
|
|
86
98
|
// authenticated command will fail even though `valid` reads true.
|
|
99
|
+
// 'mismatch' overrides 'ok' when the live account isn't the one that
|
|
100
|
+
// owns this project - `valid` still reads true (the token IS valid).
|
|
87
101
|
auth: (auth || usingEnvToken()) ? {
|
|
88
102
|
email: auth?.email,
|
|
103
|
+
account: probe.account,
|
|
89
104
|
source: usingEnvToken() ? 'agent-token' : 'session',
|
|
90
|
-
valid: usingEnvToken() ? probe !== 'rejected' : !sessionExpired(),
|
|
91
|
-
probe,
|
|
105
|
+
valid: usingEnvToken() ? probe.state !== 'rejected' : !sessionExpired(),
|
|
106
|
+
probe: accountMismatch ? 'mismatch' : probe.state,
|
|
92
107
|
} : null,
|
|
93
108
|
plugin: hookCheck,
|
|
94
109
|
}, null, 2));
|
|
@@ -102,28 +117,42 @@ export const statusCommand = new Command('status')
|
|
|
102
117
|
console.log(`${muted('Account:')} ${config.accountSlug}`);
|
|
103
118
|
console.log(`${muted('Live:')} ${liveUrl(config)}`);
|
|
104
119
|
console.log(`${muted('API:')} ${config.apiBase}`);
|
|
120
|
+
// apiBase is only what THIS project recorded - resolveApiBase() is what
|
|
121
|
+
// every real request actually uses (it can diverge via GIPITY_API_BASE,
|
|
122
|
+
// --api-base, or a disallowed host being dropped to the default). Surface
|
|
123
|
+
// the divergence rather than silently trusting the recorded value.
|
|
124
|
+
if (apiBaseInUse !== config.apiBase) {
|
|
125
|
+
console.log(`${muted('API (in use):')} ${warning(apiBaseInUse)} ${muted('(overrides .gipity.json — GIPITY_API_BASE / --api-base / allowlist)')}`);
|
|
126
|
+
}
|
|
105
127
|
if (config.agentGuid)
|
|
106
128
|
console.log(`${muted('Agent:')} ${config.agentGuid}`);
|
|
107
129
|
}
|
|
108
130
|
if (usingEnvToken()) {
|
|
109
|
-
console.log(`${muted('Auth:')} ${probe === 'rejected'
|
|
131
|
+
console.log(`${muted('Auth:')} ${probe.state === 'rejected'
|
|
110
132
|
? warning('agent API token (GIPITY_TOKEN) rejected by the server — mint a new one: gipity skill read agent-deploy')
|
|
111
|
-
: success('agent API token (GIPITY_TOKEN)')}${probe === 'unreachable' ? ` ${muted('(unverified — API unreachable)')}` : ''}`);
|
|
133
|
+
: success('agent API token (GIPITY_TOKEN)')}${probe.state === 'unreachable' ? ` ${muted('(unverified — API unreachable)')}` : ''}`);
|
|
112
134
|
}
|
|
113
135
|
else if (!auth) {
|
|
114
136
|
console.log(`${muted('Auth:')} ${warning('not logged in. Run: gipity login')}`);
|
|
115
137
|
}
|
|
116
|
-
else if (probe === 'expired') {
|
|
138
|
+
else if (probe.state === 'expired') {
|
|
117
139
|
console.log(`${muted('Auth:')} ${warning(`session expired for ${auth.email}. Run: gipity login (headless/CI: set GIPITY_TOKEN — gipity skill read agent-deploy)`)}`);
|
|
118
140
|
}
|
|
119
|
-
else if (probe === 'rejected') {
|
|
141
|
+
else if (probe.state === 'rejected') {
|
|
120
142
|
// Locally fresh but the server says no (refresh token rotated away or
|
|
121
143
|
// revoked). Without the live probe this printed a green identity while
|
|
122
144
|
// every authenticated command failed.
|
|
123
145
|
console.log(`${muted('Auth:')} ${warning(`session for ${auth.email} was rejected by the server. Run: gipity login (headless/CI: set GIPITY_TOKEN — gipity skill read agent-deploy)`)}`);
|
|
124
146
|
}
|
|
125
147
|
else {
|
|
126
|
-
console.log(`${muted('Auth:')} ${success(auth.email)}${probe === 'unreachable' ? ` ${muted('(unverified — API unreachable)')}` : ''}`);
|
|
148
|
+
console.log(`${muted('Auth:')} ${success(auth.email)}${probe.state === 'unreachable' ? ` ${muted('(unverified — API unreachable)')}` : ''}`);
|
|
149
|
+
}
|
|
150
|
+
// Source-independent (session or GIPITY_TOKEN): a mismatch under an agent
|
|
151
|
+
// token is the same wrong-account class and must not be hidden inside the
|
|
152
|
+
// cascade above, which only special-cases 'rejected' for that source.
|
|
153
|
+
if (accountMismatch) {
|
|
154
|
+
console.log(`${muted('Account:')} ${warning(`logged-in account (${probe.account}) differs from this project's account (${config.accountSlug}). If you didn't expect this you may be logged into the wrong account — run: gipity login`)}`);
|
|
155
|
+
console.log(muted('(If this project was shared with you via gipity rbac, this is expected.)'));
|
|
127
156
|
}
|
|
128
157
|
if (queueDelivered > 0) {
|
|
129
158
|
console.log(`${muted('Bug queue:')} ${success(`delivered ${queueDelivered} queued bug report${queueDelivered === 1 ? '' : 's'}`)}`);
|
package/dist/commands/storage.js
CHANGED
|
@@ -50,12 +50,12 @@ function printUsage(d) {
|
|
|
50
50
|
console.log(`${bold('By project')} ${muted(scope)}`);
|
|
51
51
|
for (const p of d.projects) {
|
|
52
52
|
const name = p.projectName ?? '(no project)';
|
|
53
|
-
row(name.length > 16 ? `${name.slice(0,
|
|
53
|
+
row(name.length > 16 ? `${name.slice(0, 13)}...` : name, formatBytes(p.liveBytes), `${p.liveFiles.toLocaleString()} files`);
|
|
54
54
|
}
|
|
55
55
|
if (hidden > 0) {
|
|
56
56
|
const shownBytes = d.projects.reduce((n, p) => n + p.liveBytes, 0);
|
|
57
57
|
const shownFiles = d.projects.reduce((n, p) => n + p.liveFiles, 0);
|
|
58
|
-
row(
|
|
58
|
+
row(`...and ${hidden.toLocaleString()} more`, formatBytes(totals.liveBytes - shownBytes), `${(totals.liveFiles - shownFiles).toLocaleString()} files`);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
const r = d.versionRetention;
|
package/dist/commands/sync.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
2
3
|
import { sync } from '../sync.js';
|
|
3
4
|
import { createProgressReporter } from '../progress.js';
|
|
4
5
|
import { error as clrError, muted } from '../colors.js';
|
|
6
|
+
import { findWindowsTwinProject, isWsl, wslPathToWindows } from '../utils.js';
|
|
7
|
+
import { getConfigPath } from '../config.js';
|
|
5
8
|
export const syncCommand = new Command('sync')
|
|
6
9
|
.description('Sync files')
|
|
7
10
|
.addHelpText('after', '\nAdd a .gipityignore at the project root to exclude paths (gitignore-style).')
|
|
@@ -30,6 +33,18 @@ export const syncCommand = new Command('sync')
|
|
|
30
33
|
// local). Surface that here as a helpful hint - not a red error - with
|
|
31
34
|
// the one command that resolves it. Only `gipity sync` shows this;
|
|
32
35
|
// deploy/test/sandbox stay silent (their internal sync defers quietly).
|
|
36
|
+
// WSL trap: a same-named folder under C:\Users\...\GipityProjects looks
|
|
37
|
+
// like "the project" from Windows Explorer, but files dropped there
|
|
38
|
+
// never sync. When a twin exists, say so - "Up to date." alone reads as
|
|
39
|
+
// "your new files are in" when they're sitting on the Windows side.
|
|
40
|
+
if (isWsl()) {
|
|
41
|
+
const configPath = getConfigPath();
|
|
42
|
+
const twin = configPath ? findWindowsTwinProject(dirname(configPath)) : null;
|
|
43
|
+
if (twin) {
|
|
44
|
+
console.log(muted(`\nNote: a Windows-side copy of this project exists at ${wslPathToWindows(twin)} and is NOT synced. ` +
|
|
45
|
+
`If you added files there, move them into this folder so they sync.`));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
33
48
|
if (result.deferredDeletes > 0) {
|
|
34
49
|
console.log(muted(`\n${result.deferredDeletes} file${result.deferredDeletes > 1 ? 's' : ''} on Gipity ` +
|
|
35
50
|
`${result.deferredDeletes > 1 ? 'are' : 'is'} not present locally and ${result.deferredDeletes > 1 ? 'were' : 'was'} left untouched. ` +
|
package/dist/commands/test.js
CHANGED
|
@@ -149,7 +149,7 @@ async function pollTestStatus(projectGuid, runGuid, opts) {
|
|
|
149
149
|
const tally = data.passed + data.failed > 0
|
|
150
150
|
? ` (${data.passed} passed${data.failed > 0 ? `, ${data.failed} failed` : ''} so far)`
|
|
151
151
|
: '';
|
|
152
|
-
console.log(muted(`
|
|
152
|
+
console.log(muted(` ... still running - ${progress}${tally}, ${elapsed}s elapsed`));
|
|
153
153
|
if (now - startTime >= LONG_RUN_MS && !longRunHintShown) {
|
|
154
154
|
longRunHintShown = true;
|
|
155
155
|
console.log(muted(' Note: progressing, not hung. LLM-backed tests can take minutes. To verify one function fast, use `gipity fn call <name>`; narrow this suite with `gipity test <path>`.'));
|
package/dist/commands/token.js
CHANGED
|
@@ -4,7 +4,12 @@ import { bold, muted, success, warning } from '../colors.js';
|
|
|
4
4
|
import { run, printList } from '../helpers/index.js';
|
|
5
5
|
export const tokenCommand = new Command('token')
|
|
6
6
|
.description('Manage API tokens')
|
|
7
|
-
.addHelpText('after',
|
|
7
|
+
.addHelpText('after', `
|
|
8
|
+
Long-lived agent API tokens (gip_at_*) for headless agents and CI - they drive
|
|
9
|
+
the gipity CLI as YOU.
|
|
10
|
+
|
|
11
|
+
To let a script or cron write to one app instead, mint a project API key:
|
|
12
|
+
gipity key create "my script" --role editor (sent as X-Api-Key).`);
|
|
8
13
|
const fmtDate = (d) => (d ? new Date(d).toLocaleDateString() : 'never');
|
|
9
14
|
tokenCommand
|
|
10
15
|
.command('create')
|
|
@@ -19,7 +19,7 @@ import { confirm, getAutoConfirm } from '../utils.js';
|
|
|
19
19
|
import { bold, brand, dim, success, error as clrError, muted } from '../colors.js';
|
|
20
20
|
import * as relayState from '../relay/state.js';
|
|
21
21
|
import { planFor, UnsupportedPlatformError } from '../relay/installers.js';
|
|
22
|
-
import { GIPITY_PLUGIN_ID, GIPITY_MARKETPLACE_NAME, stripGipityHooks, grokInstallState, agentSkillsState, AGENTS_SKILLS_DIR } from '../setup.js';
|
|
22
|
+
import { GIPITY_PLUGIN_ID, GIPITY_MARKETPLACE_NAME, stripGipityHooks, grokInstallState, agentSkillsState, AGENTS_SKILLS_DIR, agySkillsState, AGY_SKILLS_DIR, } from '../setup.js';
|
|
23
23
|
/** Remove Gipity's entries from the user-scope Claude Code settings: the
|
|
24
24
|
* plugin enablement, the marketplace registration, and any legacy hook
|
|
25
25
|
* blocks older CLI versions wrote there. Surgical - everything else in the
|
|
@@ -240,6 +240,19 @@ export const uninstallCommand = new Command('uninstall')
|
|
|
240
240
|
}
|
|
241
241
|
console.log(`${success(`Removed ${agentSkills.skills.length} Gipity skills from ~/.agents/skills.`)}`);
|
|
242
242
|
}
|
|
243
|
+
// 4c. Remove the skills the CLI copied into Antigravity's own global
|
|
244
|
+
// skill root (~/.gemini/config/skills - a different directory from
|
|
245
|
+
// Codex's ~/.agents/skills, see setup.ts).
|
|
246
|
+
const agySkills = agySkillsState();
|
|
247
|
+
if (agySkills.skills.length) {
|
|
248
|
+
for (const name of agySkills.skills) {
|
|
249
|
+
try {
|
|
250
|
+
rmSync(join(AGY_SKILLS_DIR, name), { recursive: true, force: true });
|
|
251
|
+
}
|
|
252
|
+
catch { /* best-effort */ }
|
|
253
|
+
}
|
|
254
|
+
console.log(`${success(`Removed ${agySkills.skills.length} Gipity skills from ~/.gemini/config/skills.`)}`);
|
|
255
|
+
}
|
|
243
256
|
// 5. Wipe ~/.gipity/ (this also removes the agent-hooks scripts and the
|
|
244
257
|
// agent-skills manifest, which live under it).
|
|
245
258
|
if (existsSync(gipityDir)) {
|
package/dist/commands/upload.js
CHANGED
|
@@ -65,7 +65,7 @@ token-signed serve url instead (reachable only by holders of the url).`)
|
|
|
65
65
|
};
|
|
66
66
|
const data = opts.json
|
|
67
67
|
? await doUpload()
|
|
68
|
-
: await withSpinner(`Uploading ${filename} (${formatSize(size)})
|
|
68
|
+
: await withSpinner(`Uploading ${filename} (${formatSize(size)})...`, doUpload, { done: null });
|
|
69
69
|
if (opts.json) {
|
|
70
70
|
console.log(JSON.stringify(data));
|
|
71
71
|
return;
|
|
@@ -186,7 +186,7 @@ workflowCommand
|
|
|
186
186
|
}));
|
|
187
187
|
workflowCommand
|
|
188
188
|
.command('runs <name> [runGuid]')
|
|
189
|
-
.description('List recent runs, or pass a run guid (wr_
|
|
189
|
+
.description('List recent runs, or pass a run guid (wr_...) to see that run\'s per-step outputs')
|
|
190
190
|
.option('--json', 'Output as JSON')
|
|
191
191
|
.action((name, runGuid, _opts, cmd) => run('Runs', async () => {
|
|
192
192
|
const opts = mergedOpts(cmd);
|
|
@@ -51,6 +51,7 @@ import { getConfig } from '../config.js';
|
|
|
51
51
|
import { parseTranscript as parseClaudeTranscript, } from '../capture/sources/claude-code.js';
|
|
52
52
|
import { parseTranscript as parseCodexTranscript } from '../capture/sources/codex.js';
|
|
53
53
|
import { parseTranscript as parseGrokTranscript } from '../capture/sources/grok.js';
|
|
54
|
+
import { parseTranscript as parseAgyTranscript } from '../capture/sources/agy.js';
|
|
54
55
|
const CAPTURE_DIR = join(homedir(), '.gipity', 'capture-state');
|
|
55
56
|
const INGEST_BATCH_MAX = 100; // server caps at 200; stay comfortably under
|
|
56
57
|
export const CAPTURE_SOURCES = {
|
|
@@ -77,6 +78,14 @@ export const CAPTURE_SOURCES = {
|
|
|
77
78
|
return join(homedir(), '.grok', 'sessions', encodeURIComponent(cwd), hook.session_id, 'chat_history.jsonl');
|
|
78
79
|
},
|
|
79
80
|
},
|
|
81
|
+
agy: {
|
|
82
|
+
serverSource: 'agy',
|
|
83
|
+
displayName: 'Antigravity',
|
|
84
|
+
// agy hook payloads always carry transcriptPath directly (no derivation
|
|
85
|
+
// needed, unlike Grok) - the agy-specific hook wrapper normalizes it into
|
|
86
|
+
// this HookInput's transcript_path before invoking this runner.
|
|
87
|
+
parse: (content, afterUuid, hook) => parseAgyTranscript(content, afterUuid, { conversationId: hook.session_id }),
|
|
88
|
+
},
|
|
80
89
|
};
|
|
81
90
|
// PostToolUse fires after every tool call. We flush on it so a session that is
|
|
82
91
|
// killed/crashes mid-run (e.g. a long headless `gipity claude -p` build that
|
|
@@ -430,9 +439,10 @@ async function handleSessionEnd(convGuid, src, hook) {
|
|
|
430
439
|
if (hook.session_id)
|
|
431
440
|
deleteSessionMap(hook.session_id);
|
|
432
441
|
}
|
|
433
|
-
// Codex
|
|
434
|
-
// never cleaned by handleSessionEnd. Sweep anything
|
|
435
|
-
// - the files are tiny, so a generous TTL is fine; a
|
|
442
|
+
// Codex and agy have no SessionEnd-equivalent hook, so their capture-state/
|
|
443
|
+
// session-map files are never cleaned by handleSessionEnd. Sweep anything
|
|
444
|
+
// stale on the way through - the files are tiny, so a generous TTL is fine; a
|
|
445
|
+
// live session's state is
|
|
436
446
|
// rewritten on every flush and never gets this old.
|
|
437
447
|
const STATE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
438
448
|
function sweepStaleState() {
|
|
@@ -455,8 +465,10 @@ function sweepStaleState() {
|
|
|
455
465
|
}
|
|
456
466
|
/** Normalize a raw hook payload to snake_case HookInput. Claude Code and
|
|
457
467
|
* Codex deliver snake_case (`session_id`, `transcript_path`, `cwd`); Grok
|
|
458
|
-
* Build delivers camelCase (`sessionId`, `hookEventName`, …)
|
|
459
|
-
*
|
|
468
|
+
* Build delivers camelCase (`sessionId`, `hookEventName`, …); agy calls its
|
|
469
|
+
* session id `conversationId` (its own hook payload has no session/cwd/event
|
|
470
|
+
* fields under any other name - see cli/src/agents/agy.ts). Accept all of
|
|
471
|
+
* these so one runner serves every harness. */
|
|
460
472
|
export function normalizeHookInput(raw) {
|
|
461
473
|
if (!raw || typeof raw !== 'object')
|
|
462
474
|
return {};
|
|
@@ -469,7 +481,7 @@ export function normalizeHookInput(raw) {
|
|
|
469
481
|
return undefined;
|
|
470
482
|
};
|
|
471
483
|
const hook = {
|
|
472
|
-
session_id: pick('session_id', 'sessionId'),
|
|
484
|
+
session_id: pick('session_id', 'sessionId', 'conversationId'),
|
|
473
485
|
transcript_path: pick('transcript_path', 'transcriptPath'),
|
|
474
486
|
cwd: pick('cwd', 'workingDirectory'),
|
|
475
487
|
hook_event_name: pick('hook_event_name', 'hookEventName'),
|
|
@@ -509,8 +521,8 @@ async function main() {
|
|
|
509
521
|
if (derived)
|
|
510
522
|
hook.transcript_path = derived;
|
|
511
523
|
}
|
|
512
|
-
// Opportunistic hygiene: Codex never
|
|
513
|
-
// files are TTL-swept instead of deleted at end-of-session.
|
|
524
|
+
// Opportunistic hygiene: Codex and agy never fire session-end, so their
|
|
525
|
+
// state files are TTL-swept instead of deleted at end-of-session.
|
|
514
526
|
sweepStaleState();
|
|
515
527
|
const convGuid = await resolveConvGuid(hook, src.serverSource);
|
|
516
528
|
if (!convGuid)
|