@ghl-ai/aw 0.1.73-beta.1 → 0.1.73-beta.2
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.
|
@@ -11,15 +11,23 @@
|
|
|
11
11
|
|
|
12
12
|
'use strict';
|
|
13
13
|
|
|
14
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
buildEvent,
|
|
16
|
+
sendAsync,
|
|
17
|
+
isDisabled,
|
|
18
|
+
findRecentSdlcSessionForProject,
|
|
19
|
+
} = require('../lib/aw-usage-telemetry');
|
|
15
20
|
|
|
16
21
|
function buildCommitCreatedEvent({ commitHash = 'unknown', branch = 'unknown', cwd = process.cwd() } = {}) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
const linkedSession = findRecentSdlcSessionForProject(cwd);
|
|
23
|
+
const event = buildEvent({
|
|
24
|
+
cwd,
|
|
25
|
+
...(linkedSession?.session_id ? { session_id: linkedSession.session_id } : {}),
|
|
26
|
+
}, 'commit_created', {
|
|
20
27
|
commit_hash: commitHash,
|
|
21
28
|
commit_sha: commitHash,
|
|
22
29
|
branch,
|
|
30
|
+
...(linkedSession?.session_id ? { linked_session_source: 'project_recent_sdlc_session' } : {}),
|
|
23
31
|
});
|
|
24
32
|
|
|
25
33
|
// Override harness to 'git' since this fires from a git hook, not a harness.
|
|
@@ -109,7 +109,9 @@ function processPromptSubmitInput(input, deps = {}) {
|
|
|
109
109
|
|
|
110
110
|
if (slash) {
|
|
111
111
|
persistSkill(getSessionId(input), input?.turn_id || null, slash);
|
|
112
|
-
persistSlashCmd(getSessionId(input), slash
|
|
112
|
+
persistSlashCmd(getSessionId(input), slash, {
|
|
113
|
+
cwd: input?.cwd || (input?.workspace_roots && input.workspace_roots[0]) || null,
|
|
114
|
+
});
|
|
113
115
|
events.push({
|
|
114
116
|
eventType: 'skill_invoked',
|
|
115
117
|
payload: {
|
|
@@ -21,6 +21,7 @@ const SENDER_SCRIPT = path.join(__dirname, '..', 'hooks', 'aw-usage-telemetry-se
|
|
|
21
21
|
const AW_HOME = path.join(os.homedir(), '.aw');
|
|
22
22
|
const CONFIG_PATH = path.join(AW_HOME, 'telemetry', 'config.json');
|
|
23
23
|
const SESSION_DIR = path.join(AW_HOME, 'telemetry', 'sessions');
|
|
24
|
+
const SESSION_PROJECT_DIR = path.join(SESSION_DIR, 'by-project');
|
|
24
25
|
const DEDUPE_DIR = path.join(os.tmpdir(), 'aw-usage-telemetry-dedupe');
|
|
25
26
|
|
|
26
27
|
// ── Git config cache (once per process) ──────────────────────────────
|
|
@@ -157,6 +158,16 @@ function computeProjectHash(cwd) {
|
|
|
157
158
|
return crypto.createHash('sha256').update(cwd).digest('hex').slice(0, 16);
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
function normalizeProjectHash(value) {
|
|
162
|
+
return typeof value === 'string' && /^[a-f0-9]{16}$/i.test(value) ? value.toLowerCase() : null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function resolveProjectHash(context) {
|
|
166
|
+
if (!context || typeof context !== 'object') return null;
|
|
167
|
+
return normalizeProjectHash(context.project_hash || context.projectHash)
|
|
168
|
+
|| computeProjectHash(context.cwd);
|
|
169
|
+
}
|
|
170
|
+
|
|
160
171
|
// ── Session file cleanup ─────────────────────────────────────────────
|
|
161
172
|
// Prune session files older than SESSION_MAX_AGE_MS to prevent unbounded growth.
|
|
162
173
|
// Called once per session start — best-effort, never blocks.
|
|
@@ -241,21 +252,40 @@ function readSessionSkill(sessionId, turnId) {
|
|
|
241
252
|
// to the originating /aw:* invocation. Separate from `last_skill` because
|
|
242
253
|
// the source is the prompt, not the tool, and the lifetime is the whole
|
|
243
254
|
// session (not just one turn).
|
|
244
|
-
function
|
|
255
|
+
function writeProjectSessionIndex(projectHash, sessionId, slashCommand) {
|
|
256
|
+
if (!projectHash || !sessionId || !slashCommand?.is_sdlc_stage) return;
|
|
257
|
+
try {
|
|
258
|
+
fs.mkdirSync(SESSION_PROJECT_DIR, { recursive: true });
|
|
259
|
+
fs.writeFileSync(path.join(SESSION_PROJECT_DIR, `${projectHash}.json`), JSON.stringify({
|
|
260
|
+
project_hash: projectHash,
|
|
261
|
+
session_id: sessionId,
|
|
262
|
+
command_namespace: slashCommand.command_namespace || null,
|
|
263
|
+
command_name: slashCommand.command_name,
|
|
264
|
+
is_sdlc_stage: Boolean(slashCommand.is_sdlc_stage),
|
|
265
|
+
updated_at: new Date().toISOString(),
|
|
266
|
+
}));
|
|
267
|
+
} catch { /* ignore */ }
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function persistSessionSlashCommand(sessionId, slashCommand, context = {}) {
|
|
245
271
|
if (!sessionId || !slashCommand?.command_name) return;
|
|
246
272
|
try {
|
|
247
273
|
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
248
274
|
const state = readSessionState(sessionId);
|
|
275
|
+
const projectHash = resolveProjectHash(context) || state.project_hash || null;
|
|
249
276
|
fs.writeFileSync(path.join(SESSION_DIR, sessionId + '.json'), JSON.stringify({
|
|
250
277
|
...state,
|
|
278
|
+
...(projectHash ? { project_hash: projectHash } : {}),
|
|
251
279
|
last_slash_command: {
|
|
252
280
|
command_namespace: slashCommand.command_namespace || null,
|
|
253
281
|
command_name: slashCommand.command_name,
|
|
254
282
|
command_args: slashCommand.command_args || '',
|
|
255
283
|
is_sdlc_stage: Boolean(slashCommand.is_sdlc_stage),
|
|
284
|
+
...(projectHash ? { project_hash: projectHash } : {}),
|
|
256
285
|
updated_at: new Date().toISOString(),
|
|
257
286
|
},
|
|
258
287
|
}));
|
|
288
|
+
writeProjectSessionIndex(projectHash, sessionId, slashCommand);
|
|
259
289
|
} catch { /* ignore */ }
|
|
260
290
|
}
|
|
261
291
|
|
|
@@ -265,6 +295,59 @@ function readSessionLastSlashCommand(sessionId) {
|
|
|
265
295
|
return cmd;
|
|
266
296
|
}
|
|
267
297
|
|
|
298
|
+
function parseUpdatedAt(value) {
|
|
299
|
+
const millis = Date.parse(value || '');
|
|
300
|
+
return Number.isFinite(millis) ? millis : 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function isRecentSdlcSessionCandidate(candidate, projectHash, maxAgeMs) {
|
|
304
|
+
if (!candidate?.session_id) return false;
|
|
305
|
+
if (!candidate?.is_sdlc_stage) return false;
|
|
306
|
+
if (candidate.project_hash !== projectHash) return false;
|
|
307
|
+
const updatedAt = parseUpdatedAt(candidate.updated_at);
|
|
308
|
+
if (!updatedAt) return false;
|
|
309
|
+
return Date.now() - updatedAt <= maxAgeMs;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function readProjectSessionIndex(projectHash, maxAgeMs) {
|
|
313
|
+
try {
|
|
314
|
+
const candidate = JSON.parse(fs.readFileSync(path.join(SESSION_PROJECT_DIR, `${projectHash}.json`), 'utf8'));
|
|
315
|
+
return isRecentSdlcSessionCandidate(candidate, projectHash, maxAgeMs) ? candidate : null;
|
|
316
|
+
} catch {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function findRecentSdlcSessionForProject(cwd, maxAgeMs = SESSION_MAX_AGE_MS) {
|
|
322
|
+
const projectHash = computeProjectHash(cwd);
|
|
323
|
+
if (!projectHash) return null;
|
|
324
|
+
|
|
325
|
+
const indexed = readProjectSessionIndex(projectHash, maxAgeMs);
|
|
326
|
+
if (indexed) return indexed;
|
|
327
|
+
|
|
328
|
+
let best = null;
|
|
329
|
+
try {
|
|
330
|
+
const entries = fs.readdirSync(SESSION_DIR);
|
|
331
|
+
for (const entry of entries) {
|
|
332
|
+
if (!entry.endsWith('.json')) continue;
|
|
333
|
+
const sessionId = entry.slice(0, -'.json'.length);
|
|
334
|
+
const state = readSessionState(sessionId);
|
|
335
|
+
const cmd = state.last_slash_command;
|
|
336
|
+
const candidate = {
|
|
337
|
+
session_id: sessionId,
|
|
338
|
+
project_hash: cmd?.project_hash || state.project_hash || null,
|
|
339
|
+
is_sdlc_stage: Boolean(cmd?.is_sdlc_stage),
|
|
340
|
+
updated_at: cmd?.updated_at || state.updated_at || null,
|
|
341
|
+
};
|
|
342
|
+
if (!isRecentSdlcSessionCandidate(candidate, projectHash, maxAgeMs)) continue;
|
|
343
|
+
if (!best || parseUpdatedAt(candidate.updated_at) > parseUpdatedAt(best.updated_at)) {
|
|
344
|
+
best = candidate;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch { /* ignore */ }
|
|
348
|
+
return best;
|
|
349
|
+
}
|
|
350
|
+
|
|
268
351
|
// ── Short-TTL dedupe guards ──────────────────────────────────────────
|
|
269
352
|
|
|
270
353
|
function normalizeDedupePart(value) {
|
|
@@ -503,6 +586,7 @@ module.exports = {
|
|
|
503
586
|
readSessionSkill,
|
|
504
587
|
persistSessionSlashCommand,
|
|
505
588
|
readSessionLastSlashCommand,
|
|
589
|
+
findRecentSdlcSessionForProject,
|
|
506
590
|
readLastAssistantFromTranscript,
|
|
507
591
|
resolvePromptText,
|
|
508
592
|
getAwCliVersionDetails,
|