@proletariat/cli 0.3.105 → 0.3.109
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/commands/agent/cleanup.js +13 -1
- package/dist/commands/agent/cleanup.js.map +1 -1
- package/dist/commands/claude/index.js +2 -2
- package/dist/commands/claude/index.js.map +1 -1
- package/dist/commands/feedback/list.js +4 -9
- package/dist/commands/feedback/list.js.map +1 -1
- package/dist/commands/feedback/submit.js +4 -9
- package/dist/commands/feedback/submit.js.map +1 -1
- package/dist/commands/feedback/view.js +4 -9
- package/dist/commands/feedback/view.js.map +1 -1
- package/dist/commands/gc.d.ts +1 -0
- package/dist/commands/gc.js +34 -1
- package/dist/commands/gc.js.map +1 -1
- package/dist/commands/notify/connect.d.ts +34 -0
- package/dist/commands/notify/connect.js +166 -0
- package/dist/commands/notify/connect.js.map +1 -0
- package/dist/commands/notify/disconnect.d.ts +16 -0
- package/dist/commands/notify/disconnect.js +45 -0
- package/dist/commands/notify/disconnect.js.map +1 -0
- package/dist/commands/notify/list.d.ts +15 -0
- package/dist/commands/notify/list.js +101 -0
- package/dist/commands/notify/list.js.map +1 -0
- package/dist/commands/notify/rules/add.d.ts +26 -0
- package/dist/commands/notify/rules/add.js +95 -0
- package/dist/commands/notify/rules/add.js.map +1 -0
- package/dist/commands/notify/rules/list.d.ts +14 -0
- package/dist/commands/notify/rules/list.js +82 -0
- package/dist/commands/notify/rules/list.js.map +1 -0
- package/dist/commands/notify/rules/remove.d.ts +16 -0
- package/dist/commands/notify/rules/remove.js +44 -0
- package/dist/commands/notify/rules/remove.js.map +1 -0
- package/dist/commands/notify/test.d.ts +16 -0
- package/dist/commands/notify/test.js +63 -0
- package/dist/commands/notify/test.js.map +1 -0
- package/dist/commands/orchestrate/index.js +11 -6
- package/dist/commands/orchestrate/index.js.map +1 -1
- package/dist/commands/orchestrate/machine.d.ts +29 -0
- package/dist/commands/orchestrate/machine.js +230 -0
- package/dist/commands/orchestrate/machine.js.map +1 -0
- package/dist/commands/pr/checks.js +4 -8
- package/dist/commands/pr/checks.js.map +1 -1
- package/dist/commands/pr/close.js +4 -8
- package/dist/commands/pr/close.js.map +1 -1
- package/dist/commands/pr/create.js +4 -8
- package/dist/commands/pr/create.js.map +1 -1
- package/dist/commands/pr/index.js +1 -1
- package/dist/commands/pr/index.js.map +1 -1
- package/dist/commands/pr/link.js +4 -8
- package/dist/commands/pr/link.js.map +1 -1
- package/dist/commands/pr/list.js +5 -9
- package/dist/commands/pr/list.js.map +1 -1
- package/dist/commands/pr/merge.d.ts +5 -0
- package/dist/commands/pr/merge.js +35 -11
- package/dist/commands/pr/merge.js.map +1 -1
- package/dist/commands/pr/status.js +5 -4
- package/dist/commands/pr/status.js.map +1 -1
- package/dist/commands/qa/index.js +2 -2
- package/dist/commands/qa/index.js.map +1 -1
- package/dist/commands/repo/create.js +4 -8
- package/dist/commands/repo/create.js.map +1 -1
- package/dist/commands/session/list.js +81 -41
- package/dist/commands/session/list.js.map +1 -1
- package/dist/commands/session/poke.d.ts +1 -11
- package/dist/commands/session/poke.js +20 -78
- package/dist/commands/session/poke.js.map +1 -1
- package/dist/commands/session/prune.js +3 -0
- package/dist/commands/session/prune.js.map +1 -1
- package/dist/commands/ticket/move.d.ts +12 -0
- package/dist/commands/ticket/move.js +75 -2
- package/dist/commands/ticket/move.js.map +1 -1
- package/dist/commands/work/drop.js +2 -2
- package/dist/commands/work/drop.js.map +1 -1
- package/dist/commands/work/ready.js +3 -3
- package/dist/commands/work/ready.js.map +1 -1
- package/dist/commands/work/rebase.js +4 -8
- package/dist/commands/work/rebase.js.map +1 -1
- package/dist/commands/work/run.d.ts +58 -0
- package/dist/commands/work/run.js +411 -0
- package/dist/commands/work/run.js.map +1 -0
- package/dist/commands/work/ship.d.ts +6 -0
- package/dist/commands/work/ship.js +93 -51
- package/dist/commands/work/ship.js.map +1 -1
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +126 -13
- package/dist/commands/work/start.js.map +1 -1
- package/dist/lib/agents/commands.d.ts +6 -0
- package/dist/lib/agents/commands.js +55 -2
- package/dist/lib/agents/commands.js.map +1 -1
- package/dist/lib/database/credential-store.js +2 -3
- package/dist/lib/database/credential-store.js.map +1 -1
- package/dist/lib/database/db-safety.d.ts +25 -0
- package/dist/lib/database/db-safety.js +35 -0
- package/dist/lib/database/db-safety.js.map +1 -1
- package/dist/lib/database/driver.js +6 -12
- package/dist/lib/database/driver.js.map +1 -1
- package/dist/lib/database/drizzle-schema.d.ts +3 -3
- package/dist/lib/database/drizzle.js +3 -3
- package/dist/lib/database/drizzle.js.map +1 -1
- package/dist/lib/database/index.d.ts +1 -1
- package/dist/lib/database/index.js +1 -1
- package/dist/lib/database/index.js.map +1 -1
- package/dist/lib/database/migrations/0021_notification_system.d.ts +2 -0
- package/dist/lib/database/migrations/0021_notification_system.js +39 -0
- package/dist/lib/database/migrations/0021_notification_system.js.map +1 -0
- package/dist/lib/database/migrations/0022_hook_mode_tiers.d.ts +11 -0
- package/dist/lib/database/migrations/0022_hook_mode_tiers.js +53 -0
- package/dist/lib/database/migrations/0022_hook_mode_tiers.js.map +1 -0
- package/dist/lib/database/migrations/index.js +4 -0
- package/dist/lib/database/migrations/index.js.map +1 -1
- package/dist/lib/database/pmo-bootstrap.js +6 -2
- package/dist/lib/database/pmo-bootstrap.js.map +1 -1
- package/dist/lib/database/workspace.js +5 -13
- package/dist/lib/database/workspace.js.map +1 -1
- package/dist/lib/events/emitting-runner.js +10 -0
- package/dist/lib/events/emitting-runner.js.map +1 -1
- package/dist/lib/execution/cc-version.d.ts +62 -0
- package/dist/lib/execution/cc-version.js +103 -0
- package/dist/lib/execution/cc-version.js.map +1 -0
- package/dist/lib/execution/devcontainer.js +2 -1
- package/dist/lib/execution/devcontainer.js.map +1 -1
- package/dist/lib/execution/runners/devcontainer.js +4 -1
- package/dist/lib/execution/runners/devcontainer.js.map +1 -1
- package/dist/lib/execution/runners/docker-management.js +10 -46
- package/dist/lib/execution/runners/docker-management.js.map +1 -1
- package/dist/lib/execution/runners/orchestrator.js +13 -39
- package/dist/lib/execution/runners/orchestrator.js.map +1 -1
- package/dist/lib/execution/session-utils.d.ts +88 -1
- package/dist/lib/execution/session-utils.js +120 -46
- package/dist/lib/execution/session-utils.js.map +1 -1
- package/dist/lib/execution/storage.js +20 -2
- package/dist/lib/execution/storage.js.map +1 -1
- package/dist/lib/flags/resolver.d.ts +8 -1
- package/dist/lib/flags/resolver.js +35 -2
- package/dist/lib/flags/resolver.js.map +1 -1
- package/dist/lib/gc/cascade.d.ts +99 -0
- package/dist/lib/gc/cascade.js +357 -0
- package/dist/lib/gc/cascade.js.map +1 -0
- package/dist/lib/gc/config.d.ts +69 -0
- package/dist/lib/gc/config.js +134 -0
- package/dist/lib/gc/config.js.map +1 -0
- package/dist/lib/gc/index.d.ts +36 -0
- package/dist/lib/gc/index.js +209 -1
- package/dist/lib/gc/index.js.map +1 -1
- package/dist/lib/init/index.js +10 -1
- package/dist/lib/init/index.js.map +1 -1
- package/dist/lib/machine-db.d.ts +144 -0
- package/dist/lib/machine-db.js +338 -0
- package/dist/lib/machine-db.js.map +1 -0
- package/dist/lib/machine-orchestrator.d.ts +35 -0
- package/dist/lib/machine-orchestrator.js +139 -0
- package/dist/lib/machine-orchestrator.js.map +1 -0
- package/dist/lib/notifications/dispatcher.d.ts +29 -0
- package/dist/lib/notifications/dispatcher.js +281 -0
- package/dist/lib/notifications/dispatcher.js.map +1 -0
- package/dist/lib/notifications/index.d.ts +13 -0
- package/dist/lib/notifications/index.js +18 -0
- package/dist/lib/notifications/index.js.map +1 -0
- package/dist/lib/notifications/manager.d.ts +46 -0
- package/dist/lib/notifications/manager.js +200 -0
- package/dist/lib/notifications/manager.js.map +1 -0
- package/dist/lib/notifications/storage.d.ts +60 -0
- package/dist/lib/notifications/storage.js +182 -0
- package/dist/lib/notifications/storage.js.map +1 -0
- package/dist/lib/notifications/types.d.ts +126 -0
- package/dist/lib/notifications/types.js +16 -0
- package/dist/lib/notifications/types.js.map +1 -0
- package/dist/lib/orchestrate/actions.js +182 -3
- package/dist/lib/orchestrate/actions.js.map +1 -1
- package/dist/lib/orchestrate/config-loader.js +9 -35
- package/dist/lib/orchestrate/config-loader.js.map +1 -1
- package/dist/lib/orchestrate/engine.d.ts +53 -1
- package/dist/lib/orchestrate/engine.js +74 -3
- package/dist/lib/orchestrate/engine.js.map +1 -1
- package/dist/lib/orchestrate/escalation.d.ts +87 -0
- package/dist/lib/orchestrate/escalation.js +63 -0
- package/dist/lib/orchestrate/escalation.js.map +1 -0
- package/dist/lib/orchestrate/index.d.ts +2 -0
- package/dist/lib/orchestrate/index.js +1 -0
- package/dist/lib/orchestrate/index.js.map +1 -1
- package/dist/lib/orchestrate/llm-agent.d.ts +101 -0
- package/dist/lib/orchestrate/llm-agent.js +295 -0
- package/dist/lib/orchestrate/llm-agent.js.map +1 -0
- package/dist/lib/orchestrate/presets.d.ts +4 -3
- package/dist/lib/orchestrate/presets.js +13 -8
- package/dist/lib/orchestrate/presets.js.map +1 -1
- package/dist/lib/orchestrate/types.d.ts +7 -1
- package/dist/lib/orchestrate/types.js +1 -0
- package/dist/lib/orchestrate/types.js.map +1 -1
- package/dist/lib/pmo/base-command.js +1 -1
- package/dist/lib/pmo/base-command.js.map +1 -1
- package/dist/lib/pmo/find-pmo.d.ts +8 -0
- package/dist/lib/pmo/find-pmo.js +63 -2
- package/dist/lib/pmo/find-pmo.js.map +1 -1
- package/dist/lib/pmo/index.d.ts +1 -1
- package/dist/lib/pmo/index.js +1 -1
- package/dist/lib/pmo/index.js.map +1 -1
- package/dist/lib/pmo/pmo-context.js +1 -1
- package/dist/lib/pmo/pmo-context.js.map +1 -1
- package/dist/lib/pmo/storage/index.js +2 -1
- package/dist/lib/pmo/storage/index.js.map +1 -1
- package/dist/lib/pr/index.d.ts +32 -0
- package/dist/lib/pr/index.js +45 -0
- package/dist/lib/pr/index.js.map +1 -1
- package/dist/lib/registry/index.js +0 -1
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/signal-handler.d.ts +8 -0
- package/dist/lib/signal-handler.js +33 -0
- package/dist/lib/signal-handler.js.map +1 -1
- package/dist/lib/themes.js +8 -1
- package/dist/lib/themes.js.map +1 -1
- package/dist/lib/work-lifecycle/container-cleanup-hook.d.ts +17 -7
- package/dist/lib/work-lifecycle/container-cleanup-hook.js +64 -11
- package/dist/lib/work-lifecycle/container-cleanup-hook.js.map +1 -1
- package/dist/lib/work-lifecycle/hooks/executor.d.ts +22 -2
- package/dist/lib/work-lifecycle/hooks/executor.js +46 -8
- package/dist/lib/work-lifecycle/hooks/executor.js.map +1 -1
- package/dist/lib/work-lifecycle/hooks/manager.d.ts +62 -3
- package/dist/lib/work-lifecycle/hooks/manager.js +285 -4
- package/dist/lib/work-lifecycle/hooks/manager.js.map +1 -1
- package/dist/lib/work-lifecycle/hooks/types.d.ts +36 -5
- package/dist/lib/work-lifecycle/hooks/types.js +1 -1
- package/dist/lib/work-lifecycle/hooks/types.js.map +1 -1
- package/dist/lib/workspace-resolution.d.ts +73 -0
- package/dist/lib/workspace-resolution.js +188 -0
- package/dist/lib/workspace-resolution.js.map +1 -0
- package/oclif.manifest.json +2830 -2142
- package/package.json +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3-Tier Escalation Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Routes hook actions through a 3-tier decision hierarchy:
|
|
5
|
+
*
|
|
6
|
+
* Tier 1 (auto): Deterministic — fires immediately, no decision needed.
|
|
7
|
+
* Tier 2 (llm): LLM orchestrator decides — approve/deny/escalate.
|
|
8
|
+
* Tier 3 (human): Human decides — final authority.
|
|
9
|
+
*
|
|
10
|
+
* Escalation flows upward: auto → llm → human.
|
|
11
|
+
* Timeout on LLM response auto-escalates to human.
|
|
12
|
+
*/
|
|
13
|
+
import type { DecisionTier, LlmDecision } from '../work-lifecycle/hooks/types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Context provided to LLM and human decision-makers.
|
|
16
|
+
*/
|
|
17
|
+
export interface EscalationContext {
|
|
18
|
+
/** The hook name requesting a decision */
|
|
19
|
+
hookName: string;
|
|
20
|
+
/** The event that triggered the hook */
|
|
21
|
+
event: string;
|
|
22
|
+
/** The action to be executed if approved */
|
|
23
|
+
action: string;
|
|
24
|
+
/** Full event context (ticket, PR, branch, etc.) */
|
|
25
|
+
ctx: Record<string, unknown>;
|
|
26
|
+
/** Optional per-hook config */
|
|
27
|
+
config?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A pending LLM decision queued when no onLlmDecision callback is provided.
|
|
31
|
+
*/
|
|
32
|
+
export interface PendingLlmDecision extends EscalationContext {
|
|
33
|
+
/** When the decision was queued */
|
|
34
|
+
queuedAt: number;
|
|
35
|
+
/** Timeout in ms after which the decision auto-escalates to human */
|
|
36
|
+
timeoutMs: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A pending human escalation queued when no onHumanEscalation callback is provided.
|
|
40
|
+
*/
|
|
41
|
+
export interface PendingHumanEscalation extends EscalationContext {
|
|
42
|
+
/** When the escalation was queued */
|
|
43
|
+
queuedAt: number;
|
|
44
|
+
/** Reason for escalation (direct human-mode hook, LLM escalated, LLM timed out) */
|
|
45
|
+
reason: EscalationReason;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Why a hook was escalated to human tier.
|
|
49
|
+
*/
|
|
50
|
+
export type EscalationReason = 'direct' | 'llm_escalate' | 'llm_timeout';
|
|
51
|
+
/**
|
|
52
|
+
* Callbacks for the escalation pipeline.
|
|
53
|
+
*/
|
|
54
|
+
export interface EscalationCallbacks {
|
|
55
|
+
/**
|
|
56
|
+
* Called when a Tier 2 (LLM) decision is needed.
|
|
57
|
+
* Should return the LLM's decision: approve, deny, or escalate.
|
|
58
|
+
* If not provided, decisions are queued in pendingLlmDecisions.
|
|
59
|
+
*/
|
|
60
|
+
onLlmDecision?: (context: EscalationContext) => Promise<LlmDecision>;
|
|
61
|
+
/**
|
|
62
|
+
* Called when a Tier 3 (human) decision is needed.
|
|
63
|
+
* Should return true to approve, false to deny.
|
|
64
|
+
* If not provided, decisions are queued in pendingHumanEscalations.
|
|
65
|
+
*/
|
|
66
|
+
onHumanEscalation?: (context: EscalationContext, reason: EscalationReason) => Promise<boolean>;
|
|
67
|
+
}
|
|
68
|
+
/** Default timeout for LLM decisions before auto-escalating to human (5 minutes). */
|
|
69
|
+
export declare const DEFAULT_LLM_TIMEOUT_MS: number;
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a HookMode to its decision tier.
|
|
72
|
+
*
|
|
73
|
+
* - auto, notify → Tier 1 (auto)
|
|
74
|
+
* - llm → Tier 2 (llm)
|
|
75
|
+
* - confirm, human → Tier 3 (human)
|
|
76
|
+
* - off → Tier 1 (auto) — handled separately as skip
|
|
77
|
+
*/
|
|
78
|
+
export declare function modeToTier(mode: string): DecisionTier;
|
|
79
|
+
/**
|
|
80
|
+
* Check if a pending LLM decision has timed out.
|
|
81
|
+
*/
|
|
82
|
+
export declare function isLlmDecisionTimedOut(decision: PendingLlmDecision, now?: number): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Get all timed-out LLM decisions from a list, returning them
|
|
85
|
+
* as human escalations with reason 'llm_timeout'.
|
|
86
|
+
*/
|
|
87
|
+
export declare function getTimedOutLlmDecisions(decisions: PendingLlmDecision[], now?: number): PendingHumanEscalation[];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3-Tier Escalation Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Routes hook actions through a 3-tier decision hierarchy:
|
|
5
|
+
*
|
|
6
|
+
* Tier 1 (auto): Deterministic — fires immediately, no decision needed.
|
|
7
|
+
* Tier 2 (llm): LLM orchestrator decides — approve/deny/escalate.
|
|
8
|
+
* Tier 3 (human): Human decides — final authority.
|
|
9
|
+
*
|
|
10
|
+
* Escalation flows upward: auto → llm → human.
|
|
11
|
+
* Timeout on LLM response auto-escalates to human.
|
|
12
|
+
*/
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Configuration
|
|
15
|
+
// =============================================================================
|
|
16
|
+
/** Default timeout for LLM decisions before auto-escalating to human (5 minutes). */
|
|
17
|
+
export const DEFAULT_LLM_TIMEOUT_MS = 5 * 60 * 1000;
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Utility Functions
|
|
20
|
+
// =============================================================================
|
|
21
|
+
/**
|
|
22
|
+
* Resolve a HookMode to its decision tier.
|
|
23
|
+
*
|
|
24
|
+
* - auto, notify → Tier 1 (auto)
|
|
25
|
+
* - llm → Tier 2 (llm)
|
|
26
|
+
* - confirm, human → Tier 3 (human)
|
|
27
|
+
* - off → Tier 1 (auto) — handled separately as skip
|
|
28
|
+
*/
|
|
29
|
+
export function modeToTier(mode) {
|
|
30
|
+
switch (mode) {
|
|
31
|
+
case 'llm':
|
|
32
|
+
return 'llm';
|
|
33
|
+
case 'confirm':
|
|
34
|
+
case 'human':
|
|
35
|
+
return 'human';
|
|
36
|
+
default:
|
|
37
|
+
return 'auto';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if a pending LLM decision has timed out.
|
|
42
|
+
*/
|
|
43
|
+
export function isLlmDecisionTimedOut(decision, now = Date.now()) {
|
|
44
|
+
return (now - decision.queuedAt) >= decision.timeoutMs;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get all timed-out LLM decisions from a list, returning them
|
|
48
|
+
* as human escalations with reason 'llm_timeout'.
|
|
49
|
+
*/
|
|
50
|
+
export function getTimedOutLlmDecisions(decisions, now = Date.now()) {
|
|
51
|
+
return decisions
|
|
52
|
+
.filter(d => isLlmDecisionTimedOut(d, now))
|
|
53
|
+
.map(d => ({
|
|
54
|
+
hookName: d.hookName,
|
|
55
|
+
event: d.event,
|
|
56
|
+
action: d.action,
|
|
57
|
+
ctx: d.ctx,
|
|
58
|
+
config: d.config,
|
|
59
|
+
queuedAt: d.queuedAt,
|
|
60
|
+
reason: 'llm_timeout',
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=escalation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escalation.js","sourceRoot":"","sources":["../../../src/lib/orchestrate/escalation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAuEH,gFAAgF;AAChF,gBAAgB;AAChB,gFAAgF;AAEhF,qFAAqF;AACrF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;AAEnD,gFAAgF;AAChF,oBAAoB;AACpB,gFAAgF;AAEhF;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,KAAK;YACR,OAAO,KAAK,CAAA;QACd,KAAK,SAAS,CAAC;QACf,KAAK,OAAO;YACV,OAAO,OAAO,CAAA;QAChB;YACE,OAAO,MAAM,CAAA;IACjB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAA4B,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IAC1F,OAAO,CAAC,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAA;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,SAA+B,EAC/B,MAAc,IAAI,CAAC,GAAG,EAAE;IAExB,OAAO,SAAS;SACb,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,qBAAqB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACT,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,MAAM,EAAE,aAAiC;KAC1C,CAAC,CAAC,CAAA;AACP,CAAC"}
|
|
@@ -16,3 +16,5 @@ export { OrchestratePoller } from './poller.js';
|
|
|
16
16
|
export type { PollerOptions } from './poller.js';
|
|
17
17
|
export { SimplePoller } from './simple-poller.js';
|
|
18
18
|
export type { SimplePollerOptions, PollChange, PollResult } from './simple-poller.js';
|
|
19
|
+
export { createLlmDecisionHandler, buildDecisionPrompt, parseDecisionResponse, gatherDecisionContext, fetchPrDiff, fetchTicketDescription, fetchTestOutput, invokeClaudeLlm, } from './llm-agent.js';
|
|
20
|
+
export type { DecisionContext, LlmAgentOptions } from './llm-agent.js';
|
|
@@ -12,4 +12,5 @@ export { ACTION_HANDLERS, executeBuiltinAction, } from './actions.js';
|
|
|
12
12
|
export { OrchestrateEngine, initOrchestrateEngine, getOrchestrateEngine, stopOrchestrateEngine, } from './engine.js';
|
|
13
13
|
export { OrchestratePoller } from './poller.js';
|
|
14
14
|
export { SimplePoller } from './simple-poller.js';
|
|
15
|
+
export { createLlmDecisionHandler, buildDecisionPrompt, parseDecisionResponse, gatherDecisionContext, fetchPrDiff, fetchTicketDescription, fetchTestOutput, invokeClaudeLlm, } from './llm-agent.js';
|
|
15
16
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/orchestrate/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,OAAO,EACP,SAAS,GACV,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,GAClB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,eAAe,EACf,oBAAoB,GACrB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AAIpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAG/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/orchestrate/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,OAAO,EACP,SAAS,GACV,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,GAClB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,eAAe,EACf,oBAAoB,GACrB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AAIpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAG/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,eAAe,GAChB,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Orchestrator Agent (Tier 2)
|
|
3
|
+
*
|
|
4
|
+
* Implements the onLlmDecision callback for the 3-tier supervision tree.
|
|
5
|
+
* When the daemon encounters a hook with mode=llm, this agent:
|
|
6
|
+
*
|
|
7
|
+
* 1. Gathers rich context — PR diff, ticket description, test output
|
|
8
|
+
* 2. Builds a structured decision prompt
|
|
9
|
+
* 3. Calls claude -p (print mode) to get a judgment
|
|
10
|
+
* 4. Parses the response to return approve/deny/escalate
|
|
11
|
+
*
|
|
12
|
+
* This is the bridge between the deterministic daemon (tier 1) and
|
|
13
|
+
* human escalation (tier 3). Without it, tier 2 is a pass-through.
|
|
14
|
+
*/
|
|
15
|
+
import type { EscalationContext } from './escalation.js';
|
|
16
|
+
import type { LlmDecision } from '../work-lifecycle/hooks/types.js';
|
|
17
|
+
/**
|
|
18
|
+
* Enriched context gathered for the LLM decision.
|
|
19
|
+
*/
|
|
20
|
+
export interface DecisionContext {
|
|
21
|
+
/** The original escalation context */
|
|
22
|
+
escalation: EscalationContext;
|
|
23
|
+
/** PR diff (truncated if too large) */
|
|
24
|
+
prDiff?: string;
|
|
25
|
+
/** Ticket description */
|
|
26
|
+
ticketDescription?: string;
|
|
27
|
+
/** Recent test/CI output */
|
|
28
|
+
testOutput?: string;
|
|
29
|
+
/** Branch name */
|
|
30
|
+
branch?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Options for creating the LLM decision handler.
|
|
34
|
+
*/
|
|
35
|
+
export interface LlmAgentOptions {
|
|
36
|
+
/** Logger function */
|
|
37
|
+
log?: (msg: string) => void;
|
|
38
|
+
/** Maximum characters for PR diff in prompt (default: 8000) */
|
|
39
|
+
maxDiffChars?: number;
|
|
40
|
+
/** Maximum characters for test output in prompt (default: 4000) */
|
|
41
|
+
maxTestOutputChars?: number;
|
|
42
|
+
/** Timeout in ms for the LLM call (default: 60000) */
|
|
43
|
+
llmCallTimeoutMs?: number;
|
|
44
|
+
/** Override the LLM invocation for testing */
|
|
45
|
+
invokeLlm?: (prompt: string) => string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Fetch the PR diff via `gh pr diff`.
|
|
49
|
+
* Returns undefined if no PR number is available or the command fails.
|
|
50
|
+
*/
|
|
51
|
+
export declare function fetchPrDiff(prNumber: number | undefined, maxChars: number): string | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Fetch the ticket description via `prlt ticket show`.
|
|
54
|
+
* Returns undefined if no ticket ID is available or the command fails.
|
|
55
|
+
*/
|
|
56
|
+
export declare function fetchTicketDescription(ticketId: string | undefined): string | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Fetch recent CI/test output via `gh pr checks` or `gh run list`.
|
|
59
|
+
* Returns undefined if unavailable.
|
|
60
|
+
*/
|
|
61
|
+
export declare function fetchTestOutput(prNumber: number | undefined, maxChars: number): string | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Gather enriched context from all available sources.
|
|
64
|
+
*/
|
|
65
|
+
export declare function gatherDecisionContext(escalation: EscalationContext, options: {
|
|
66
|
+
maxDiffChars: number;
|
|
67
|
+
maxTestOutputChars: number;
|
|
68
|
+
}): DecisionContext;
|
|
69
|
+
/**
|
|
70
|
+
* Build the decision prompt for the LLM.
|
|
71
|
+
*
|
|
72
|
+
* The prompt is structured to get a clear approve/deny/escalate response
|
|
73
|
+
* with reasoning.
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildDecisionPrompt(context: DecisionContext): string;
|
|
76
|
+
/**
|
|
77
|
+
* Parse the LLM response to extract the decision.
|
|
78
|
+
*
|
|
79
|
+
* Looks for APPROVE, DENY, or ESCALATE as the first meaningful word.
|
|
80
|
+
* Falls back to 'escalate' if the response is ambiguous.
|
|
81
|
+
*/
|
|
82
|
+
export declare function parseDecisionResponse(response: string): LlmDecision;
|
|
83
|
+
/**
|
|
84
|
+
* Invoke the LLM via `claude -p` (print mode).
|
|
85
|
+
* Returns the raw response text.
|
|
86
|
+
*/
|
|
87
|
+
export declare function invokeClaudeLlm(prompt: string, timeoutMs: number): string;
|
|
88
|
+
/**
|
|
89
|
+
* Create an onLlmDecision callback for the OrchestrateEngine.
|
|
90
|
+
*
|
|
91
|
+
* This is the main entry point — wire this into the engine to activate tier 2.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* const engine = new OrchestrateEngine({
|
|
96
|
+
* db,
|
|
97
|
+
* onLlmDecision: createLlmDecisionHandler({ log: console.log }),
|
|
98
|
+
* })
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function createLlmDecisionHandler(options?: LlmAgentOptions): (context: EscalationContext) => Promise<LlmDecision>;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Orchestrator Agent (Tier 2)
|
|
3
|
+
*
|
|
4
|
+
* Implements the onLlmDecision callback for the 3-tier supervision tree.
|
|
5
|
+
* When the daemon encounters a hook with mode=llm, this agent:
|
|
6
|
+
*
|
|
7
|
+
* 1. Gathers rich context — PR diff, ticket description, test output
|
|
8
|
+
* 2. Builds a structured decision prompt
|
|
9
|
+
* 3. Calls claude -p (print mode) to get a judgment
|
|
10
|
+
* 4. Parses the response to return approve/deny/escalate
|
|
11
|
+
*
|
|
12
|
+
* This is the bridge between the deterministic daemon (tier 1) and
|
|
13
|
+
* human escalation (tier 3). Without it, tier 2 is a pass-through.
|
|
14
|
+
*/
|
|
15
|
+
import { execSync } from 'node:child_process';
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Constants
|
|
18
|
+
// =============================================================================
|
|
19
|
+
const DEFAULT_MAX_DIFF_CHARS = 8000;
|
|
20
|
+
const DEFAULT_MAX_TEST_OUTPUT_CHARS = 4000;
|
|
21
|
+
const DEFAULT_LLM_CALL_TIMEOUT_MS = 60_000;
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Context Gathering (pure functions, individually testable)
|
|
24
|
+
// =============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Fetch the PR diff via `gh pr diff`.
|
|
27
|
+
* Returns undefined if no PR number is available or the command fails.
|
|
28
|
+
*/
|
|
29
|
+
export function fetchPrDiff(prNumber, maxChars) {
|
|
30
|
+
if (!prNumber)
|
|
31
|
+
return undefined;
|
|
32
|
+
try {
|
|
33
|
+
const diff = execSync(`gh pr diff ${prNumber} --color=never`, {
|
|
34
|
+
timeout: 15_000,
|
|
35
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
36
|
+
encoding: 'utf-8',
|
|
37
|
+
});
|
|
38
|
+
if (diff.length > maxChars) {
|
|
39
|
+
return diff.slice(0, maxChars) + `\n\n... [diff truncated at ${maxChars} chars, ${diff.length} total]`;
|
|
40
|
+
}
|
|
41
|
+
return diff || undefined;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Fetch the ticket description via `prlt ticket show`.
|
|
49
|
+
* Returns undefined if no ticket ID is available or the command fails.
|
|
50
|
+
*/
|
|
51
|
+
export function fetchTicketDescription(ticketId) {
|
|
52
|
+
if (!ticketId)
|
|
53
|
+
return undefined;
|
|
54
|
+
try {
|
|
55
|
+
const output = execSync(`prlt ticket show ${ticketId} --json`, {
|
|
56
|
+
timeout: 10_000,
|
|
57
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
58
|
+
encoding: 'utf-8',
|
|
59
|
+
});
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(output);
|
|
62
|
+
const parts = [];
|
|
63
|
+
if (parsed.result?.title)
|
|
64
|
+
parts.push(`Title: ${parsed.result.title}`);
|
|
65
|
+
if (parsed.result?.description)
|
|
66
|
+
parts.push(`Description: ${parsed.result.description}`);
|
|
67
|
+
if (parsed.result?.acceptanceCriteria?.length) {
|
|
68
|
+
parts.push(`Acceptance Criteria:\n${parsed.result.acceptanceCriteria.map((ac) => ` - ${ac}`).join('\n')}`);
|
|
69
|
+
}
|
|
70
|
+
return parts.length > 0 ? parts.join('\n') : output;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return output || undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Fetch recent CI/test output via `gh pr checks` or `gh run list`.
|
|
82
|
+
* Returns undefined if unavailable.
|
|
83
|
+
*/
|
|
84
|
+
export function fetchTestOutput(prNumber, maxChars) {
|
|
85
|
+
if (!prNumber)
|
|
86
|
+
return undefined;
|
|
87
|
+
try {
|
|
88
|
+
const output = execSync(`gh pr checks ${prNumber}`, {
|
|
89
|
+
timeout: 15_000,
|
|
90
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
91
|
+
encoding: 'utf-8',
|
|
92
|
+
});
|
|
93
|
+
if (output.length > maxChars) {
|
|
94
|
+
return output.slice(0, maxChars) + `\n\n... [output truncated at ${maxChars} chars]`;
|
|
95
|
+
}
|
|
96
|
+
return output || undefined;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Gather enriched context from all available sources.
|
|
104
|
+
*/
|
|
105
|
+
export function gatherDecisionContext(escalation, options) {
|
|
106
|
+
const prNumber = escalation.ctx.pr;
|
|
107
|
+
const ticketId = escalation.ctx.ticket;
|
|
108
|
+
const branch = escalation.ctx.branch;
|
|
109
|
+
return {
|
|
110
|
+
escalation,
|
|
111
|
+
prDiff: fetchPrDiff(prNumber, options.maxDiffChars),
|
|
112
|
+
ticketDescription: fetchTicketDescription(ticketId),
|
|
113
|
+
testOutput: fetchTestOutput(prNumber, options.maxTestOutputChars),
|
|
114
|
+
branch,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// =============================================================================
|
|
118
|
+
// Prompt Construction (pure function, testable)
|
|
119
|
+
// =============================================================================
|
|
120
|
+
/**
|
|
121
|
+
* Build the decision prompt for the LLM.
|
|
122
|
+
*
|
|
123
|
+
* The prompt is structured to get a clear approve/deny/escalate response
|
|
124
|
+
* with reasoning.
|
|
125
|
+
*/
|
|
126
|
+
export function buildDecisionPrompt(context) {
|
|
127
|
+
const { escalation, prDiff, ticketDescription, testOutput, branch } = context;
|
|
128
|
+
const sections = [];
|
|
129
|
+
sections.push(`# Orchestrator Decision Required
|
|
130
|
+
|
|
131
|
+
You are an autonomous orchestrator agent making a tier-2 decision in a 3-tier supervision tree.
|
|
132
|
+
Your role: review the context and decide whether to APPROVE, DENY, or ESCALATE this action.
|
|
133
|
+
|
|
134
|
+
## Decision
|
|
135
|
+
|
|
136
|
+
- **Hook:** ${escalation.hookName}
|
|
137
|
+
- **Event:** ${escalation.event}
|
|
138
|
+
- **Action:** ${escalation.action}
|
|
139
|
+
${branch ? `- **Branch:** ${branch}` : ''}
|
|
140
|
+
${escalation.config ? `- **Config:** ${JSON.stringify(escalation.config)}` : ''}`);
|
|
141
|
+
if (ticketDescription) {
|
|
142
|
+
sections.push(`## Ticket Context
|
|
143
|
+
|
|
144
|
+
${ticketDescription}`);
|
|
145
|
+
}
|
|
146
|
+
if (prDiff) {
|
|
147
|
+
sections.push(`## PR Diff
|
|
148
|
+
|
|
149
|
+
\`\`\`diff
|
|
150
|
+
${prDiff}
|
|
151
|
+
\`\`\``);
|
|
152
|
+
}
|
|
153
|
+
if (testOutput) {
|
|
154
|
+
sections.push(`## CI/Test Status
|
|
155
|
+
|
|
156
|
+
\`\`\`
|
|
157
|
+
${testOutput}
|
|
158
|
+
\`\`\``);
|
|
159
|
+
}
|
|
160
|
+
sections.push(`## Decision Guidelines
|
|
161
|
+
|
|
162
|
+
Consider these factors:
|
|
163
|
+
1. **Safety** — Does the action risk data loss, downtime, or breaking changes?
|
|
164
|
+
2. **Context match** — Does the action align with the event and ticket context?
|
|
165
|
+
3. **CI status** — Are tests passing? Is the diff reasonable?
|
|
166
|
+
4. **Scope** — Is the action proportional to the event?
|
|
167
|
+
|
|
168
|
+
### When to APPROVE
|
|
169
|
+
- CI is green and the action is a natural next step (e.g., merge after green CI)
|
|
170
|
+
- Agent spawn for a ticket that's ready and has clear requirements
|
|
171
|
+
- Respawn of a died agent with retries remaining
|
|
172
|
+
|
|
173
|
+
### When to DENY
|
|
174
|
+
- CI is failing and the action would advance the pipeline (e.g., merge a failing PR)
|
|
175
|
+
- The diff contains suspicious changes (secrets, large deletions, unrelated files)
|
|
176
|
+
- The action doesn't match the event context
|
|
177
|
+
|
|
178
|
+
### When to ESCALATE
|
|
179
|
+
- The situation is ambiguous or high-stakes (production deployments, large refactors)
|
|
180
|
+
- You lack sufficient context to make a confident decision
|
|
181
|
+
- The action involves irreversible operations on shared resources
|
|
182
|
+
|
|
183
|
+
## Response Format
|
|
184
|
+
|
|
185
|
+
Respond with EXACTLY one of these words on the first line, followed by your reasoning:
|
|
186
|
+
|
|
187
|
+
APPROVE
|
|
188
|
+
DENY
|
|
189
|
+
ESCALATE
|
|
190
|
+
|
|
191
|
+
Then explain your reasoning briefly.`);
|
|
192
|
+
return sections.join('\n\n');
|
|
193
|
+
}
|
|
194
|
+
// =============================================================================
|
|
195
|
+
// Response Parsing (pure function, testable)
|
|
196
|
+
// =============================================================================
|
|
197
|
+
/**
|
|
198
|
+
* Parse the LLM response to extract the decision.
|
|
199
|
+
*
|
|
200
|
+
* Looks for APPROVE, DENY, or ESCALATE as the first meaningful word.
|
|
201
|
+
* Falls back to 'escalate' if the response is ambiguous.
|
|
202
|
+
*/
|
|
203
|
+
export function parseDecisionResponse(response) {
|
|
204
|
+
const trimmed = response.trim();
|
|
205
|
+
if (!trimmed)
|
|
206
|
+
return 'escalate';
|
|
207
|
+
// Check the first non-empty line for the decision keyword
|
|
208
|
+
const lines = trimmed.split('\n');
|
|
209
|
+
for (const line of lines) {
|
|
210
|
+
const cleaned = line.trim().toUpperCase();
|
|
211
|
+
if (!cleaned)
|
|
212
|
+
continue;
|
|
213
|
+
if (cleaned.startsWith('APPROVE'))
|
|
214
|
+
return 'approve';
|
|
215
|
+
if (cleaned.startsWith('DENY'))
|
|
216
|
+
return 'deny';
|
|
217
|
+
if (cleaned.startsWith('ESCALATE'))
|
|
218
|
+
return 'escalate';
|
|
219
|
+
// Only check the first non-empty line
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
// Fallback: scan the full response for keywords (less reliable)
|
|
223
|
+
const upper = trimmed.toUpperCase();
|
|
224
|
+
if (upper.includes('APPROVE') && !upper.includes('DENY') && !upper.includes('ESCALATE')) {
|
|
225
|
+
return 'approve';
|
|
226
|
+
}
|
|
227
|
+
if (upper.includes('DENY') && !upper.includes('APPROVE') && !upper.includes('ESCALATE')) {
|
|
228
|
+
return 'deny';
|
|
229
|
+
}
|
|
230
|
+
// When in doubt, escalate to human
|
|
231
|
+
return 'escalate';
|
|
232
|
+
}
|
|
233
|
+
// =============================================================================
|
|
234
|
+
// LLM Invocation
|
|
235
|
+
// =============================================================================
|
|
236
|
+
/**
|
|
237
|
+
* Invoke the LLM via `claude -p` (print mode).
|
|
238
|
+
* Returns the raw response text.
|
|
239
|
+
*/
|
|
240
|
+
export function invokeClaudeLlm(prompt, timeoutMs) {
|
|
241
|
+
return execSync(`claude -p --output-format text`, {
|
|
242
|
+
input: prompt,
|
|
243
|
+
timeout: timeoutMs,
|
|
244
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
245
|
+
encoding: 'utf-8',
|
|
246
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
// =============================================================================
|
|
250
|
+
// Factory: onLlmDecision callback
|
|
251
|
+
// =============================================================================
|
|
252
|
+
/**
|
|
253
|
+
* Create an onLlmDecision callback for the OrchestrateEngine.
|
|
254
|
+
*
|
|
255
|
+
* This is the main entry point — wire this into the engine to activate tier 2.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```ts
|
|
259
|
+
* const engine = new OrchestrateEngine({
|
|
260
|
+
* db,
|
|
261
|
+
* onLlmDecision: createLlmDecisionHandler({ log: console.log }),
|
|
262
|
+
* })
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export function createLlmDecisionHandler(options = {}) {
|
|
266
|
+
const log = options.log ?? (() => { });
|
|
267
|
+
const maxDiffChars = options.maxDiffChars ?? DEFAULT_MAX_DIFF_CHARS;
|
|
268
|
+
const maxTestOutputChars = options.maxTestOutputChars ?? DEFAULT_MAX_TEST_OUTPUT_CHARS;
|
|
269
|
+
const llmCallTimeoutMs = options.llmCallTimeoutMs ?? DEFAULT_LLM_CALL_TIMEOUT_MS;
|
|
270
|
+
const callLlm = options.invokeLlm ?? ((prompt) => invokeClaudeLlm(prompt, llmCallTimeoutMs));
|
|
271
|
+
return async (context) => {
|
|
272
|
+
log(`[llm-agent] Decision requested: ${context.hookName} → ${context.action} (event: ${context.event})`);
|
|
273
|
+
try {
|
|
274
|
+
// 1. Gather enriched context
|
|
275
|
+
const enriched = gatherDecisionContext(context, { maxDiffChars, maxTestOutputChars });
|
|
276
|
+
// 2. Build the prompt
|
|
277
|
+
const prompt = buildDecisionPrompt(enriched);
|
|
278
|
+
log(`[llm-agent] Prompt built (${prompt.length} chars)`);
|
|
279
|
+
// 3. Call the LLM
|
|
280
|
+
const response = callLlm(prompt);
|
|
281
|
+
log(`[llm-agent] Response received (${response.length} chars)`);
|
|
282
|
+
// 4. Parse the decision
|
|
283
|
+
const decision = parseDecisionResponse(response);
|
|
284
|
+
log(`[llm-agent] Decision: ${decision} for ${context.hookName} → ${context.action}`);
|
|
285
|
+
return decision;
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
// On any error, escalate to human rather than silently approving/denying
|
|
289
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
290
|
+
log(`[llm-agent] Error during decision — escalating to human: ${errMsg}`);
|
|
291
|
+
return 'escalate';
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=llm-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-agent.js","sourceRoot":"","sources":["../../../src/lib/orchestrate/llm-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Orchestrate Presets
|
|
3
3
|
*
|
|
4
4
|
* Pre-configured hook sets for common automation levels.
|
|
5
|
+
* Maps to the 3-tier supervision tree:
|
|
5
6
|
*
|
|
6
|
-
* - aggressive: auto
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
7
|
+
* - aggressive: all auto (Tier 1) — no decision needed
|
|
8
|
+
* - supervised: safe=auto (Tier 1), destructive=llm (Tier 2)
|
|
9
|
+
* - conservative: safe=auto (Tier 1), destructive=human (Tier 3)
|
|
9
10
|
*/
|
|
10
11
|
import type { HookMode, OrchestrateEvent, PresetName } from './types.js';
|
|
11
12
|
interface PresetHook {
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Orchestrate Presets
|
|
3
3
|
*
|
|
4
4
|
* Pre-configured hook sets for common automation levels.
|
|
5
|
+
* Maps to the 3-tier supervision tree:
|
|
5
6
|
*
|
|
6
|
-
* - aggressive: auto
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
7
|
+
* - aggressive: all auto (Tier 1) — no decision needed
|
|
8
|
+
* - supervised: safe=auto (Tier 1), destructive=llm (Tier 2)
|
|
9
|
+
* - conservative: safe=auto (Tier 1), destructive=human (Tier 3)
|
|
9
10
|
*/
|
|
10
11
|
const SHARED_HOOKS = [
|
|
11
12
|
// PR lifecycle
|
|
@@ -25,6 +26,7 @@ const SHARED_HOOKS = [
|
|
|
25
26
|
{ event: 'on_agent_died', action: 'respawn', config: { max_retries: 2 } },
|
|
26
27
|
{ event: 'on_agent_died', action: 'notify' },
|
|
27
28
|
{ event: 'on_agent_idle', action: 'health-check' },
|
|
29
|
+
{ event: 'on_agent_idle', action: 'gc-sweep' },
|
|
28
30
|
// Review lifecycle
|
|
29
31
|
{ event: 'on_review_approved', action: 'notify' },
|
|
30
32
|
{ event: 'on_changes_requested', action: 'spawn-fix-agent' },
|
|
@@ -32,6 +34,8 @@ const SHARED_HOOKS = [
|
|
|
32
34
|
// CI lifecycle
|
|
33
35
|
{ event: 'on_ci_failed', action: 'notify' },
|
|
34
36
|
{ event: 'on_ci_failed', action: 'spawn-fix-agent' },
|
|
37
|
+
// Periodic cleanup
|
|
38
|
+
{ event: 'on_agent_completed', action: 'gc-sweep' },
|
|
35
39
|
];
|
|
36
40
|
/**
|
|
37
41
|
* Safe actions that can be auto-executed even in supervised mode.
|
|
@@ -50,11 +54,12 @@ const SAFE_ACTIONS = new Set([
|
|
|
50
54
|
'health-check',
|
|
51
55
|
'rebase-conflicting-prs',
|
|
52
56
|
'spawn-review-agent',
|
|
57
|
+
'gc-sweep',
|
|
53
58
|
]);
|
|
54
59
|
export const PRESETS = {
|
|
55
60
|
aggressive: {
|
|
56
61
|
name: 'aggressive',
|
|
57
|
-
description: 'Auto everything — no
|
|
62
|
+
description: 'Auto everything — Tier 1 only, no decisions needed',
|
|
58
63
|
hooks: SHARED_HOOKS.map(h => ({
|
|
59
64
|
...h,
|
|
60
65
|
mode: 'auto',
|
|
@@ -62,18 +67,18 @@ export const PRESETS = {
|
|
|
62
67
|
},
|
|
63
68
|
conservative: {
|
|
64
69
|
name: 'conservative',
|
|
65
|
-
description: '
|
|
70
|
+
description: 'Safe=auto (Tier 1), destructive=human (Tier 3)',
|
|
66
71
|
hooks: SHARED_HOOKS.map(h => ({
|
|
67
72
|
...h,
|
|
68
|
-
mode: '
|
|
73
|
+
mode: (SAFE_ACTIONS.has(h.action) ? 'auto' : 'human'),
|
|
69
74
|
})),
|
|
70
75
|
},
|
|
71
76
|
supervised: {
|
|
72
77
|
name: 'supervised',
|
|
73
|
-
description: '
|
|
78
|
+
description: 'Safe=auto (Tier 1), destructive=llm (Tier 2)',
|
|
74
79
|
hooks: SHARED_HOOKS.map(h => ({
|
|
75
80
|
...h,
|
|
76
|
-
mode: (SAFE_ACTIONS.has(h.action) ? 'auto' : '
|
|
81
|
+
mode: (SAFE_ACTIONS.has(h.action) ? 'auto' : 'llm'),
|
|
77
82
|
})),
|
|
78
83
|
},
|
|
79
84
|
};
|