forked from claude-code-best/claude-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnotifications.ts
More file actions
88 lines (79 loc) · 3 KB
/
Copy pathnotifications.ts
File metadata and controls
88 lines (79 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* Bridge for workflow status-change notifications.
*
* The engine emits events via progressEmitter.emit({ type: 'run_done', ... }),
* and the progress/store reducer records the status into RunProgress. But the
* old implementation had no code bridging status transitions to the host
* notification mechanism — the "notifies automatically on completion" promise
* in WorkflowTool's return text went unfulfilled.
*
* This module subscribes to WorkflowService.subscribe, watches status transitions
* from running → completed/failed/killed, and emits a host notification via the
* injected notifier callback (defaults to enqueuePendingNotification task-notification mode).
*/
import {
STATUS_TAG,
SUMMARY_TAG,
TASK_ID_TAG,
TASK_NOTIFICATION_TAG,
TASK_TYPE_TAG,
} from '../constants/xml.js'
import { enqueuePendingNotification } from '../utils/messageQueueManager.js'
import type { RunProgress } from './progress/store.js'
import type { WorkflowService } from './service.js'
const WORKFLOW_TASK_TYPE = 'local_workflow'
/** Notifier abstraction (lets tests inject a spy). */
export type WorkflowNotifier = (message: string) => void
const TERMINAL_STATUSES: ReadonlySet<RunProgress['status']> = new Set([
'completed',
'failed',
'killed',
])
/** Default notifier: uses the host message queue's task-notification mode. */
const defaultNotifier: WorkflowNotifier = message => {
enqueuePendingNotification({ value: message, mode: 'task-notification' })
}
export function installWorkflowNotifications(
service: WorkflowService,
notify: WorkflowNotifier = defaultNotifier,
): () => void {
const prevStatus = new Map<string, RunProgress['status'] | undefined>()
const unsubscribe = service.subscribe(() => {
const runs = service.listRuns()
for (const run of runs) {
const prev = prevStatus.get(run.runId)
// First time seeing this run: just record the current status without notifying
// (avoids treating existing historical runs as new notifications on install)
if (prev === undefined) {
prevStatus.set(run.runId, run.status)
continue
}
// Status changed + entered terminal state → emit notification
if (prev !== run.status && TERMINAL_STATUSES.has(run.status)) {
notify(buildMessage(run))
}
prevStatus.set(run.runId, run.status)
}
})
return () => {
unsubscribe()
prevStatus.clear()
}
}
function buildMessage(run: RunProgress): string {
const statusText =
run.status === 'completed'
? 'completed successfully'
: run.status === 'failed'
? 'failed'
: 'was stopped'
const errorSuffix =
run.status === 'failed' && run.error ? `: ${run.error}` : ''
const summary = `Workflow "${run.workflowName}" ${statusText}${errorSuffix}`
return `<${TASK_NOTIFICATION_TAG}>
<${TASK_ID_TAG}>${run.runId}</${TASK_ID_TAG}>
<${TASK_TYPE_TAG}>${WORKFLOW_TASK_TYPE}</${TASK_TYPE_TAG}>
<${STATUS_TAG}>${run.status}</${STATUS_TAG}>
<${SUMMARY_TAG}>${summary}</${SUMMARY_TAG}>
</${TASK_NOTIFICATION_TAG}>`
}