@juspay/shooter 1.9.3 → 1.11.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/.claude/hooks/notifier.cjs +24 -242
- package/bin/lib/tunnel-discovery.cjs +519 -0
- package/bin/shooter.cjs +204 -49
- package/build/client/_app/immutable/chunks/{heMtFUOe.js → BlgsHm7b.js} +1 -1
- package/build/client/_app/immutable/chunks/BlgsHm7b.js.br +0 -0
- package/build/client/_app/immutable/chunks/BlgsHm7b.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Woyb7K_R.js → C9URPhwn.js} +1 -1
- package/build/client/_app/immutable/chunks/C9URPhwn.js.br +0 -0
- package/build/client/_app/immutable/chunks/C9URPhwn.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Dl_UrWML.js → Ce85PELw.js} +1 -1
- package/build/client/_app/immutable/chunks/Ce85PELw.js.br +0 -0
- package/build/client/_app/immutable/chunks/{Dl_UrWML.js.gz → Ce85PELw.js.gz} +0 -0
- package/build/client/_app/immutable/entry/{app.yF5DyySX.js → app.BRJS3bSR.js} +2 -2
- package/build/client/_app/immutable/entry/app.BRJS3bSR.js.br +0 -0
- package/build/client/_app/immutable/entry/app.BRJS3bSR.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.D0irEiqS.js +1 -0
- package/build/client/_app/immutable/entry/start.D0irEiqS.js.br +2 -0
- package/build/client/_app/immutable/entry/start.D0irEiqS.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.B8ldaXrw.js → 0.BdenYwQ0.js} +1 -1
- package/build/client/_app/immutable/nodes/0.BdenYwQ0.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.BdenYwQ0.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.BaQFc0zK.js → 1.Dg4RfBlr.js} +1 -1
- package/build/client/_app/immutable/nodes/1.Dg4RfBlr.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.Dg4RfBlr.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{2.DzMPM9CG.js → 2.DW-JPzyt.js} +1 -1
- package/build/client/_app/immutable/nodes/2.DW-JPzyt.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DW-JPzyt.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.Bj2gzFb0.js → 3.bF7rX0-f.js} +1 -1
- package/build/client/_app/immutable/nodes/3.bF7rX0-f.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.bF7rX0-f.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{6.YxEM6HVV.js → 6.DSMOdSb2.js} +1 -1
- package/build/client/_app/immutable/nodes/6.DSMOdSb2.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.DSMOdSb2.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{7.CYSmE4eH.js → 7.cNNZm7gF.js} +1 -1
- package/build/client/_app/immutable/nodes/7.cNNZm7gF.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.cNNZm7gF.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{8.BiyoREYq.js → 8.DtjRdVVT.js} +1 -1
- package/build/client/_app/immutable/nodes/8.DtjRdVVT.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.DtjRdVVT.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{9.DetO0mOw.js → 9._gEbLeEN.js} +1 -1
- package/build/client/_app/immutable/nodes/9._gEbLeEN.js.br +0 -0
- package/build/client/_app/immutable/nodes/9._gEbLeEN.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/server/chunks/{0-B2E6heaO.js → 0-Cdx5OqRN.js} +2 -2
- package/build/server/chunks/{0-B2E6heaO.js.map → 0-Cdx5OqRN.js.map} +1 -1
- package/build/server/chunks/{1-G_9ph7fY.js → 1-BQErgU_W.js} +2 -2
- package/build/server/chunks/{1-G_9ph7fY.js.map → 1-BQErgU_W.js.map} +1 -1
- package/build/server/chunks/{2-Bn0siTxF.js → 2-BWmy5V7g.js} +2 -2
- package/build/server/chunks/{2-Bn0siTxF.js.map → 2-BWmy5V7g.js.map} +1 -1
- package/build/server/chunks/{3-Ckkb4IvB.js → 3-ClpS_rbl.js} +2 -2
- package/build/server/chunks/{3-Ckkb4IvB.js.map → 3-ClpS_rbl.js.map} +1 -1
- package/build/server/chunks/{6-BBZkhy6X.js → 6-CQHRlS4a.js} +2 -2
- package/build/server/chunks/{6-BBZkhy6X.js.map → 6-CQHRlS4a.js.map} +1 -1
- package/build/server/chunks/{7-BCSmnzEz.js → 7-VdriLQgS.js} +2 -2
- package/build/server/chunks/{7-BCSmnzEz.js.map → 7-VdriLQgS.js.map} +1 -1
- package/build/server/chunks/{8-D1orKzaf.js → 8-SAsJLBKt.js} +2 -2
- package/build/server/chunks/{8-D1orKzaf.js.map → 8-SAsJLBKt.js.map} +1 -1
- package/build/server/chunks/{9-BpBG1IO-.js → 9-BkY6ynKj.js} +2 -2
- package/build/server/chunks/{9-BpBG1IO-.js.map → 9-BkY6ynKj.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DkPPTUPo.js → _server.ts-BBLuxvp6.js} +5 -2
- package/build/server/chunks/{_server.ts-DkPPTUPo.js.map → _server.ts-BBLuxvp6.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BMcbwZ2r.js → _server.ts-HGYjOWF2.js} +3 -2
- package/build/server/chunks/{_server.ts-BMcbwZ2r.js.map → _server.ts-HGYjOWF2.js.map} +1 -1
- package/build/server/chunks/pending-requests-B-JNGxpk.js +96 -0
- package/build/server/chunks/pending-requests-B-JNGxpk.js.map +1 -0
- package/build/server/index.js +1 -1
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +11 -11
- package/build/server/manifest.js.map +1 -1
- package/package.json +2 -2
- package/src/lib/modules/server/apn/notification-history.ts +3 -2
- package/src/lib/modules/server/apn/pending-requests.ts +115 -27
- package/build/client/_app/immutable/chunks/Dl_UrWML.js.br +0 -0
- package/build/client/_app/immutable/chunks/Woyb7K_R.js.br +0 -0
- package/build/client/_app/immutable/chunks/Woyb7K_R.js.gz +0 -0
- package/build/client/_app/immutable/chunks/heMtFUOe.js.br +0 -0
- package/build/client/_app/immutable/chunks/heMtFUOe.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.yF5DyySX.js.br +0 -0
- package/build/client/_app/immutable/entry/app.yF5DyySX.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.qT_T2NM7.js +0 -1
- package/build/client/_app/immutable/entry/start.qT_T2NM7.js.br +0 -2
- package/build/client/_app/immutable/entry/start.qT_T2NM7.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.B8ldaXrw.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.B8ldaXrw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.BaQFc0zK.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.BaQFc0zK.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DzMPM9CG.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DzMPM9CG.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.Bj2gzFb0.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.Bj2gzFb0.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.YxEM6HVV.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.YxEM6HVV.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.CYSmE4eH.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.CYSmE4eH.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.BiyoREYq.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.BiyoREYq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.DetO0mOw.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.DetO0mOw.js.gz +0 -0
- package/build/server/chunks/pending-requests-D8UiTw7L.js +0 -44
- package/build/server/chunks/pending-requests-D8UiTw7L.js.map +0 -1
|
@@ -64,17 +64,10 @@ if (IS_CLAUDE_CODE && !API_KEY) {
|
|
|
64
64
|
process.exit(1);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
// Completion detection timeout
|
|
68
|
-
const COMPLETION_TIMEOUT = 45000; // 45 seconds
|
|
69
|
-
|
|
70
67
|
// Bidirectional permission response polling
|
|
71
68
|
const PERMISSION_TIMEOUT = parseInt(process.env.SHOOTER_PERMISSION_TIMEOUT || '120') * 1000;
|
|
72
69
|
const POLL_INTERVAL = 2000; // 2 seconds between polls
|
|
73
70
|
const RESPONSE_URL = `${BASE_URL}/api/response`;
|
|
74
|
-
const STATE_DIR = `/tmp/claude_session_tracker`;
|
|
75
|
-
|
|
76
|
-
// Global timeout tracker per project (for OpenCode)
|
|
77
|
-
const completionTimers = new Map();
|
|
78
71
|
|
|
79
72
|
// Debug logging flag
|
|
80
73
|
const DEBUG_ENABLED = process.env.SHOOTER_DEBUG === 'true';
|
|
@@ -190,18 +183,27 @@ function readStdin() {
|
|
|
190
183
|
/**
|
|
191
184
|
* Common Event Format - all events normalized to this structure
|
|
192
185
|
*
|
|
193
|
-
* eventType values:
|
|
194
|
-
* 'tool.before' - Tool is about to execute (
|
|
195
|
-
* 'tool.after' - Tool finished executing (
|
|
196
|
-
* 'session.idle' - Agent finished responding (completion timer)
|
|
186
|
+
* eventType values handled by the dispatcher:
|
|
187
|
+
* 'tool.before' - Tool is about to execute (debug log only)
|
|
188
|
+
* 'tool.after' - Tool finished executing (debug log only)
|
|
197
189
|
* 'session.start' - New session started
|
|
190
|
+
* 'session.end' - Session terminated
|
|
191
|
+
* 'subagent.start' - Subagent spawned
|
|
192
|
+
* 'subagent.stop' - Subagent finished
|
|
193
|
+
* 'user.prompt' - User submitted a prompt
|
|
198
194
|
* 'permission' - Agent needs permission to run a tool
|
|
195
|
+
* 'permission_notification' - Permission dialog opened (informational)
|
|
199
196
|
* 'question' - Agent is asking user a question / presenting options
|
|
200
197
|
* 'idle_input' - Agent is idle, waiting for user to type
|
|
201
198
|
* 'intervention' - Generic intervention needed (fallback)
|
|
202
|
-
* 'error' - An error occurred
|
|
203
|
-
* '
|
|
204
|
-
*
|
|
199
|
+
* 'error' - An error occurred (debug log only — not actionable from phone)
|
|
200
|
+
* 'context.compact' - Context about to be compacted (debug log only)
|
|
201
|
+
*
|
|
202
|
+
* Other event types emitted by adapters (e.g. 'session.idle', 'task.completed',
|
|
203
|
+
* 'teammate.idle', 'check.completion', 'session.status') fall through to the
|
|
204
|
+
* dispatcher's default case and are silently ignored — agent-idle and
|
|
205
|
+
* task-completion notifications were intentionally removed because they
|
|
206
|
+
* aren't actionable remotely.
|
|
205
207
|
*/
|
|
206
208
|
function createCommonEvent(source, eventType, data = {}) {
|
|
207
209
|
return {
|
|
@@ -412,15 +414,9 @@ function adaptOpenCodeEvent(hookEventType, hookData = {}) {
|
|
|
412
414
|
}
|
|
413
415
|
|
|
414
416
|
// ============================================
|
|
415
|
-
// SECTION 5: Session
|
|
417
|
+
// SECTION 5: Project / Session Identification
|
|
416
418
|
// ============================================
|
|
417
419
|
|
|
418
|
-
function ensureStateDir() {
|
|
419
|
-
if (!fs.existsSync(STATE_DIR)) {
|
|
420
|
-
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
420
|
function getProjectName() {
|
|
425
421
|
return path.basename(process.cwd()) || 'unknown';
|
|
426
422
|
}
|
|
@@ -432,45 +428,6 @@ function getSessionIdentifier() {
|
|
|
432
428
|
return `${projectName}_${runtime}_${pid}`;
|
|
433
429
|
}
|
|
434
430
|
|
|
435
|
-
function getSessionState() {
|
|
436
|
-
ensureStateDir();
|
|
437
|
-
const sessionId = getSessionIdentifier();
|
|
438
|
-
const stateFile = path.join(STATE_DIR, `session_state_${sessionId}.json`);
|
|
439
|
-
|
|
440
|
-
try {
|
|
441
|
-
if (fs.existsSync(stateFile)) {
|
|
442
|
-
const data = fs.readFileSync(stateFile, 'utf8');
|
|
443
|
-
return JSON.parse(data);
|
|
444
|
-
}
|
|
445
|
-
} catch (error) {
|
|
446
|
-
debugLog(`Could not read session state: ${error.message}`);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
const projectName = getProjectName();
|
|
450
|
-
return {
|
|
451
|
-
lastStopTime: null,
|
|
452
|
-
lastActivityTime: Date.now(),
|
|
453
|
-
sessionId: sessionId,
|
|
454
|
-
pendingCompletion: false,
|
|
455
|
-
project: projectName,
|
|
456
|
-
recentTools: [],
|
|
457
|
-
recentFiles: [],
|
|
458
|
-
totalToolUses: 0,
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function saveSessionState(state) {
|
|
463
|
-
ensureStateDir();
|
|
464
|
-
const sessionId = getSessionIdentifier();
|
|
465
|
-
const stateFile = path.join(STATE_DIR, `session_state_${sessionId}.json`);
|
|
466
|
-
|
|
467
|
-
try {
|
|
468
|
-
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
469
|
-
} catch (error) {
|
|
470
|
-
debugLog(`Could not save session state: ${error.message}`);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
431
|
// ============================================
|
|
475
432
|
// SECTION 6: Event Processor (Source-Agnostic)
|
|
476
433
|
// ============================================
|
|
@@ -490,10 +447,6 @@ async function processEvent(event) {
|
|
|
490
447
|
handleToolEnd(event);
|
|
491
448
|
break;
|
|
492
449
|
|
|
493
|
-
case 'session.idle':
|
|
494
|
-
handleSessionIdle(event);
|
|
495
|
-
break;
|
|
496
|
-
|
|
497
450
|
case 'session.start':
|
|
498
451
|
handleSessionStart(event);
|
|
499
452
|
break;
|
|
@@ -524,10 +477,6 @@ async function processEvent(event) {
|
|
|
524
477
|
handleError(event);
|
|
525
478
|
break;
|
|
526
479
|
|
|
527
|
-
case 'check.completion':
|
|
528
|
-
handleCheckCompletion(event);
|
|
529
|
-
break;
|
|
530
|
-
|
|
531
480
|
case 'session.end':
|
|
532
481
|
handleSessionEnd(event);
|
|
533
482
|
break;
|
|
@@ -544,14 +493,6 @@ async function processEvent(event) {
|
|
|
544
493
|
handleUserPrompt(event);
|
|
545
494
|
break;
|
|
546
495
|
|
|
547
|
-
case 'teammate.idle':
|
|
548
|
-
handleTeammateIdle(event);
|
|
549
|
-
break;
|
|
550
|
-
|
|
551
|
-
case 'task.completed':
|
|
552
|
-
handleTaskCompleted(event);
|
|
553
|
-
break;
|
|
554
|
-
|
|
555
496
|
case 'context.compact':
|
|
556
497
|
debugLog('Context compact event - tracking only');
|
|
557
498
|
break;
|
|
@@ -571,55 +512,15 @@ async function processEvent(event) {
|
|
|
571
512
|
// ============================================
|
|
572
513
|
|
|
573
514
|
function handleToolStart(event) {
|
|
574
|
-
const state = getSessionState();
|
|
575
|
-
const now = Date.now();
|
|
576
|
-
|
|
577
515
|
debugLog(`Tool starting: ${event.data.tool || 'unknown'}`);
|
|
578
|
-
|
|
579
|
-
state.lastActivityTime = now;
|
|
580
|
-
state.pendingCompletion = false;
|
|
581
|
-
|
|
582
|
-
if (!state.recentTools) state.recentTools = [];
|
|
583
|
-
if (!state.totalToolUses) state.totalToolUses = 0;
|
|
584
|
-
|
|
585
|
-
state.recentTools.unshift(event.data.tool || 'unknown');
|
|
586
|
-
state.recentTools = state.recentTools.slice(0, 10);
|
|
587
|
-
state.totalToolUses++;
|
|
588
|
-
|
|
589
|
-
saveSessionState(state);
|
|
590
|
-
|
|
591
|
-
cancelCompletionTimer(event.projectName);
|
|
592
|
-
debugLog(`Activity detected, completion timer cancelled (${state.totalToolUses} tools total)`);
|
|
593
516
|
}
|
|
594
517
|
|
|
595
518
|
function handleToolEnd(event) {
|
|
596
|
-
const state = getSessionState();
|
|
597
|
-
state.lastActivityTime = Date.now();
|
|
598
|
-
saveSessionState(state);
|
|
599
519
|
debugLog(`Tool complete: ${event.data.tool || 'unknown'}`);
|
|
600
520
|
}
|
|
601
521
|
|
|
602
|
-
function
|
|
603
|
-
|
|
604
|
-
const now = Date.now();
|
|
605
|
-
|
|
606
|
-
debugLog(`Session idle detected - starting ${COMPLETION_TIMEOUT / 1000}s completion timer`);
|
|
607
|
-
|
|
608
|
-
state.lastStopTime = now;
|
|
609
|
-
state.pendingCompletion = true;
|
|
610
|
-
saveSessionState(state);
|
|
611
|
-
|
|
612
|
-
scheduleCompletionTimer(event.projectName);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
function handleSessionStart(event) {
|
|
616
|
-
const state = getSessionState();
|
|
617
|
-
state.sessionId = Date.now().toString();
|
|
618
|
-
state.lastActivityTime = Date.now();
|
|
619
|
-
state.pendingCompletion = false;
|
|
620
|
-
saveSessionState(state);
|
|
621
|
-
cancelCompletionTimer(event.projectName);
|
|
622
|
-
debugLog(`New session started: ${state.sessionId}`);
|
|
522
|
+
function handleSessionStart(_event) {
|
|
523
|
+
debugLog(`New session started: ${getSessionIdentifier()}`);
|
|
623
524
|
}
|
|
624
525
|
|
|
625
526
|
/**
|
|
@@ -825,52 +726,20 @@ function handleError(event) {
|
|
|
825
726
|
debugLog(`Error detected (not notifying): ${event.data.message}`);
|
|
826
727
|
}
|
|
827
728
|
|
|
828
|
-
function
|
|
829
|
-
debugLog(
|
|
830
|
-
checkCompletion(event.projectName, event.source);
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
function handleSessionEnd(event) {
|
|
834
|
-
const state = getSessionState();
|
|
835
|
-
state.pendingCompletion = false;
|
|
836
|
-
saveSessionState(state);
|
|
837
|
-
cancelCompletionTimer(event.projectName);
|
|
838
|
-
debugLog('Session ended - cleaned up state');
|
|
729
|
+
function handleSessionEnd(_event) {
|
|
730
|
+
debugLog('Session ended');
|
|
839
731
|
}
|
|
840
732
|
|
|
841
733
|
function handleSubagentStart(event) {
|
|
842
|
-
const state = getSessionState();
|
|
843
|
-
state.lastActivityTime = Date.now();
|
|
844
|
-
state.pendingCompletion = false;
|
|
845
|
-
saveSessionState(state);
|
|
846
|
-
cancelCompletionTimer(event.projectName);
|
|
847
734
|
debugLog(`Subagent started: ${event.data.agentType}`);
|
|
848
735
|
}
|
|
849
736
|
|
|
850
737
|
function handleSubagentStop(event) {
|
|
851
|
-
const state = getSessionState();
|
|
852
|
-
state.lastActivityTime = Date.now();
|
|
853
|
-
saveSessionState(state);
|
|
854
738
|
debugLog(`Subagent stopped: ${event.data.agentType}`);
|
|
855
739
|
}
|
|
856
740
|
|
|
857
|
-
function handleUserPrompt(
|
|
858
|
-
|
|
859
|
-
state.lastActivityTime = Date.now();
|
|
860
|
-
state.pendingCompletion = false;
|
|
861
|
-
saveSessionState(state);
|
|
862
|
-
cancelCompletionTimer(event.projectName);
|
|
863
|
-
debugLog('User prompt submitted - activity detected');
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
function handleTeammateIdle(event) {
|
|
867
|
-
// Informational only — no user input required. Skip notification.
|
|
868
|
-
debugLog(`Teammate idle (not notifying): ${event.data.teammate}`);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
function handleTaskCompleted(event) {
|
|
872
|
-
// Informational only — task completion does not require a decision. Skip.
|
|
873
|
-
debugLog(`Task completed (not notifying): ${event.data.message}`);
|
|
741
|
+
function handleUserPrompt(_event) {
|
|
742
|
+
debugLog('User prompt submitted');
|
|
874
743
|
}
|
|
875
744
|
|
|
876
745
|
// ============================================
|
|
@@ -1291,94 +1160,7 @@ function buildQuestionNotification(event, ctx = {}) {
|
|
|
1291
1160
|
}
|
|
1292
1161
|
|
|
1293
1162
|
// ============================================
|
|
1294
|
-
// SECTION 9:
|
|
1295
|
-
// ============================================
|
|
1296
|
-
|
|
1297
|
-
function scheduleCompletionTimer(projectName) {
|
|
1298
|
-
if (IS_CLAUDE_CODE) {
|
|
1299
|
-
// Completion timer cannot work in Claude Code (each hook is a separate process)
|
|
1300
|
-
return;
|
|
1301
|
-
}
|
|
1302
|
-
debugLog(`Scheduling completion check for ${projectName}`);
|
|
1303
|
-
cancelCompletionTimer(projectName);
|
|
1304
|
-
|
|
1305
|
-
const timer = setTimeout(() => {
|
|
1306
|
-
debugLog(`Completion timer fired for ${projectName}`);
|
|
1307
|
-
checkCompletion(projectName, RUNTIME);
|
|
1308
|
-
}, COMPLETION_TIMEOUT);
|
|
1309
|
-
|
|
1310
|
-
completionTimers.set(projectName, timer);
|
|
1311
|
-
debugLog(`Completion timer scheduled (45s)`);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
function cancelCompletionTimer(projectName) {
|
|
1315
|
-
const existingTimer = completionTimers.get(projectName);
|
|
1316
|
-
if (existingTimer) {
|
|
1317
|
-
clearTimeout(existingTimer);
|
|
1318
|
-
completionTimers.delete(projectName);
|
|
1319
|
-
debugLog(`Completion timer cancelled for ${projectName}`);
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
function checkCompletion(projectName, source) {
|
|
1324
|
-
if (IS_CLAUDE_CODE) {
|
|
1325
|
-
return;
|
|
1326
|
-
}
|
|
1327
|
-
const state = getSessionState();
|
|
1328
|
-
const now = Date.now();
|
|
1329
|
-
|
|
1330
|
-
debugLog(`Checking completion status for ${projectName}`);
|
|
1331
|
-
debugLog(` pendingCompletion: ${state.pendingCompletion}`);
|
|
1332
|
-
debugLog(` lastStopTime: ${state.lastStopTime}`);
|
|
1333
|
-
debugLog(` lastActivityTime: ${state.lastActivityTime}`);
|
|
1334
|
-
|
|
1335
|
-
if (
|
|
1336
|
-
state.pendingCompletion &&
|
|
1337
|
-
state.lastStopTime &&
|
|
1338
|
-
state.lastActivityTime <= state.lastStopTime &&
|
|
1339
|
-
now - state.lastStopTime >= COMPLETION_TIMEOUT
|
|
1340
|
-
) {
|
|
1341
|
-
debugLog(`Conditions met - sending completion notification`);
|
|
1342
|
-
|
|
1343
|
-
const message = createCompletionMessage(state, projectName);
|
|
1344
|
-
|
|
1345
|
-
sendNotification(
|
|
1346
|
-
`${projectName} · Session complete`,
|
|
1347
|
-
message,
|
|
1348
|
-
'completion',
|
|
1349
|
-
source,
|
|
1350
|
-
'Agent finished'
|
|
1351
|
-
);
|
|
1352
|
-
|
|
1353
|
-
state.pendingCompletion = false;
|
|
1354
|
-
saveSessionState(state);
|
|
1355
|
-
} else {
|
|
1356
|
-
debugLog(`No completion notification needed`);
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
function createCompletionMessage(state, _projectName) {
|
|
1361
|
-
const timestamp = new Date().toLocaleTimeString();
|
|
1362
|
-
const lines = [`Finished at ${timestamp}.`];
|
|
1363
|
-
|
|
1364
|
-
const totalTools = state.totalToolUses || 0;
|
|
1365
|
-
if (totalTools > 0) {
|
|
1366
|
-
lines.push(`${totalTools} tools used.`);
|
|
1367
|
-
|
|
1368
|
-
if (state.recentTools && state.recentTools.length > 0) {
|
|
1369
|
-
lines.push(`Recent: ${state.recentTools.slice(0, 3).join(', ')}`);
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
if (state.recentFiles && state.recentFiles.length > 0) {
|
|
1373
|
-
lines.push(`Files: ${state.recentFiles.slice(0, 3).join(', ')}`);
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
return lines.join('\n');
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
// ============================================
|
|
1381
|
-
// SECTION 10: Notification Service
|
|
1163
|
+
// SECTION 9: Notification Service
|
|
1382
1164
|
// ============================================
|
|
1383
1165
|
|
|
1384
1166
|
/**
|