pi-crew 0.1.49 → 0.2.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/CHANGELOG.md +74 -1
- package/README.md +176 -781
- package/agents/analyst.md +11 -11
- package/agents/critic.md +11 -11
- package/agents/executor.md +11 -11
- package/agents/explorer.md +11 -11
- package/agents/planner.md +11 -11
- package/agents/reviewer.md +11 -11
- package/agents/security-reviewer.md +11 -11
- package/agents/test-engineer.md +11 -11
- package/agents/verifier.md +70 -11
- package/agents/writer.md +11 -11
- package/docs/actions-reference.md +595 -0
- package/docs/commands-reference.md +347 -0
- package/docs/runtime-flow.md +148 -148
- package/index.ts +6 -6
- package/package.json +99 -99
- package/skills/async-worker-recovery/SKILL.md +42 -42
- package/skills/context-artifact-hygiene/SKILL.md +52 -52
- package/skills/delegation-patterns/SKILL.md +54 -54
- package/skills/mailbox-interactive/SKILL.md +40 -40
- package/skills/model-routing-context/SKILL.md +39 -39
- package/skills/multi-perspective-review/SKILL.md +58 -58
- package/skills/observability-reliability/SKILL.md +41 -41
- package/skills/orchestration/SKILL.md +157 -157
- package/skills/ownership-session-security/SKILL.md +41 -41
- package/skills/pi-extension-lifecycle/SKILL.md +39 -39
- package/skills/requirements-to-task-packet/SKILL.md +63 -63
- package/skills/resource-discovery-config/SKILL.md +41 -41
- package/skills/runtime-state-reader/SKILL.md +44 -44
- package/skills/secure-agent-orchestration-review/SKILL.md +45 -45
- package/skills/state-mutation-locking/SKILL.md +42 -42
- package/skills/systematic-debugging/SKILL.md +67 -67
- package/skills/ui-render-performance/SKILL.md +39 -39
- package/skills/verification-before-done/SKILL.md +57 -57
- package/skills/worktree-isolation/SKILL.md +39 -39
- package/src/adapters/claude-adapter.ts +25 -0
- package/src/adapters/codex-adapter.ts +21 -0
- package/src/adapters/cursor-adapter.ts +17 -0
- package/src/adapters/export-util.ts +137 -0
- package/src/adapters/index.ts +15 -0
- package/src/adapters/registry.ts +18 -0
- package/src/adapters/types.ts +23 -0
- package/src/agents/agent-config.ts +2 -0
- package/src/agents/agent-search.ts +98 -98
- package/src/agents/discover-agents.ts +2 -1
- package/src/config/config.ts +14 -1
- package/src/config/defaults.ts +5 -5
- package/src/config/drift-detector.ts +211 -0
- package/src/config/markers.ts +327 -0
- package/src/config/resilient-parser.ts +108 -0
- package/src/config/suggestions.ts +74 -0
- package/src/extension/cross-extension-rpc.ts +103 -82
- package/src/extension/project-init.ts +36 -4
- package/src/extension/register.ts +67 -22
- package/src/extension/registration/commands.ts +77 -8
- package/src/extension/registration/subagent-tools.ts +10 -1
- package/src/extension/registration/team-tool.ts +10 -1
- package/src/extension/registration/viewers.ts +48 -34
- package/src/extension/run-bundle-schema.ts +89 -89
- package/src/extension/run-export.ts +26 -12
- package/src/extension/run-import.ts +25 -1
- package/src/extension/run-index.ts +5 -1
- package/src/extension/run-maintenance.ts +142 -68
- package/src/extension/team-manager-command.ts +10 -1
- package/src/extension/team-tool/context.ts +1 -1
- package/src/extension/team-tool/doctor.ts +28 -3
- package/src/extension/team-tool/handle-settings.ts +195 -188
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/intent-policy.ts +42 -42
- package/src/extension/team-tool/lifecycle-actions.ts +27 -8
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/run.ts +12 -1
- package/src/extension/team-tool.ts +14 -3
- package/src/i18n.ts +184 -184
- package/src/observability/exporters/otlp-exporter.ts +92 -77
- package/src/prompt/prompt-runtime.ts +72 -72
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -114
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/auto-resume.ts +100 -0
- package/src/runtime/background-runner.ts +11 -1
- package/src/runtime/cancellation-token.ts +89 -89
- package/src/runtime/cancellation.ts +61 -61
- package/src/runtime/capability-inventory.ts +116 -116
- package/src/runtime/child-pi.ts +7 -2
- package/src/runtime/compaction-summary.ts +271 -0
- package/src/runtime/completion-guard.ts +190 -190
- package/src/runtime/concurrency.ts +3 -1
- package/src/runtime/crash-recovery.ts +33 -0
- package/src/runtime/delta-conflict.ts +360 -0
- package/src/runtime/diagnostic-export.ts +3 -1
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/event-stream-bridge.ts +3 -1
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +106 -106
- package/src/runtime/heartbeat-gradient.ts +28 -28
- package/src/runtime/heartbeat-watcher.ts +124 -124
- package/src/runtime/iteration-hooks.ts +262 -0
- package/src/runtime/live-agent-control.ts +88 -88
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-extension-bridge.ts +150 -150
- package/src/runtime/live-irc.ts +92 -92
- package/src/runtime/live-session-health.ts +100 -100
- package/src/runtime/loop-gates.ts +129 -0
- package/src/runtime/metric-parser.ts +40 -0
- package/src/runtime/notebook-helpers.ts +90 -90
- package/src/runtime/orphan-sentinel.ts +7 -7
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/phase-progress.ts +217 -0
- package/src/runtime/pi-args.ts +38 -2
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/pi-spawn.ts +74 -6
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/post-checks.ts +122 -0
- package/src/runtime/process-status.ts +14 -1
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/prose-compressor.ts +164 -164
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/result-extractor.ts +121 -121
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/sensitive-paths.ts +3 -3
- package/src/runtime/session-resources.ts +25 -25
- package/src/runtime/session-snapshot.ts +59 -59
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/stream-preview.ts +177 -177
- package/src/runtime/supervisor-contact.ts +59 -59
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-graph.ts +207 -0
- package/src/runtime/task-quality.ts +207 -0
- package/src/runtime/task-runner/capabilities.ts +78 -78
- package/src/runtime/task-runner/live-executor.ts +7 -1
- package/src/runtime/task-runner/progress.ts +119 -119
- package/src/runtime/task-runner/prompt-builder.ts +1 -1
- package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/run-projection.ts +103 -103
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/team-runner.ts +126 -7
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/runtime/workflow-state.ts +187 -0
- package/src/runtime/workspace-tree.ts +298 -298
- package/src/schema/config-schema.ts +12 -0
- package/src/schema/validation-types.ts +148 -0
- package/src/skills/skill-templates.ts +374 -0
- package/src/state/active-run-registry.ts +35 -11
- package/src/state/atomic-write.ts +33 -26
- package/src/state/contracts.ts +1 -0
- package/src/state/event-reconstructor.ts +217 -0
- package/src/state/locks.ts +2 -11
- package/src/state/mailbox.ts +4 -3
- package/src/state/state-store.ts +32 -14
- package/src/state/task-claims.ts +44 -44
- package/src/state/types.ts +9 -0
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/crew-footer.ts +101 -101
- package/src/ui/crew-select-list.ts +111 -111
- package/src/ui/crew-widget.ts +9 -4
- package/src/ui/dashboard-panes/cancellation-pane.ts +42 -42
- package/src/ui/dashboard-panes/capability-pane.ts +59 -59
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -35
- package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
- package/src/ui/dashboard-panes/progress-pane.ts +11 -0
- package/src/ui/dynamic-border.ts +25 -25
- package/src/ui/layout-primitives.ts +106 -106
- package/src/ui/loaders.ts +158 -158
- package/src/ui/powerbar-publisher.ts +6 -0
- package/src/ui/render-coalescer.ts +51 -51
- package/src/ui/render-diff.ts +119 -119
- package/src/ui/render-scheduler.ts +143 -143
- package/src/ui/run-action-dispatcher.ts +10 -1
- package/src/ui/spinner.ts +17 -17
- package/src/ui/status-colors.ts +58 -58
- package/src/ui/syntax-highlight.ts +116 -116
- package/src/ui/transcript-entries.ts +258 -258
- package/src/utils/completion-dedupe.ts +63 -63
- package/src/utils/frontmatter.ts +68 -68
- package/src/utils/git.ts +262 -262
- package/src/utils/ids.ts +17 -17
- package/src/utils/incremental-reader.ts +104 -104
- package/src/utils/names.ts +27 -27
- package/src/utils/redaction.ts +44 -44
- package/src/utils/safe-paths.ts +47 -47
- package/src/utils/scan-cache.ts +136 -136
- package/src/utils/sleep.ts +40 -26
- package/src/utils/task-name-generator.ts +337 -337
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/worktree-manager.ts +11 -3
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/workflows/default.workflow.md +30 -29
- package/workflows/fast-fix.workflow.md +23 -22
- package/workflows/implementation.workflow.md +43 -38
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
- package/docs/refactor-tasks-phase3.md +0 -394
- package/docs/refactor-tasks-phase4.md +0 -564
- package/docs/refactor-tasks-phase5.md +0 -402
- package/docs/refactor-tasks-phase6.md +0 -662
- package/docs/refactor-tasks.md +0 -1484
- package/docs/research/AGENT-EXECUTION-ARCHITECTURE.md +0 -261
- package/docs/research/AGENT-LIFECYCLE-COMPARISON.md +0 -111
- package/docs/research/AUDIT_OH_MY_PI.md +0 -261
- package/docs/research/AUDIT_PI_CREW.md +0 -457
- package/docs/research/CAVEMAN-DEEP-RESEARCH.md +0 -281
- package/docs/research/COMPARISON_OH_MY_PI_VS_PI_CREW.md +0 -264
- package/docs/research/DEEP-RESEARCH-PI-POWERBAR.md +0 -343
- package/docs/research/DEEP_RESEARCH_SUBAGENT_ARCHITECTURE.md +0 -480
- package/docs/research/GAP_CLOSURE_IMPLEMENTATION_PLAN.md +0 -354
- package/docs/research/IMPLEMENTATION_PLAN.md +0 -385
- package/docs/research/LIVE-SESSION-PRODUCTION-READY-PLAN.md +0 -502
- package/docs/research/OH-MY-PI-DEEP-RESEARCH-v14.7.6.md +0 -266
- package/docs/research/REMAINING-GAPS-PLAN.md +0 -363
- package/docs/research/SESSION-SUMMARY-2026-05-08.md +0 -146
- package/docs/research/UI-RESPONSIVENESS-AUDIT.md +0 -173
- package/docs/research-awesome-agent-skills-distillation.md +0 -100
- package/docs/research-extension-examples.md +0 -297
- package/docs/research-extension-system.md +0 -324
- package/docs/research-oh-my-pi-distillation.md +0 -369
- package/docs/research-optimization-plan.md +0 -548
- package/docs/research-phase10-distillation.md +0 -199
- package/docs/research-phase11-distillation.md +0 -201
- package/docs/research-phase8-operator-experience-plan.md +0 -819
- package/docs/research-phase9-observability-reliability-plan.md +0 -1190
- package/docs/research-pi-coding-agent.md +0 -357
- package/docs/research-source-pi-crew-reference.md +0 -174
- package/docs/research-ui-optimization-plan.md +0 -480
- package/docs/source-runtime-refactor-map.md +0 -107
- package/src/utils/atomic-write.ts +0 -33
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
export interface ProgressEventSummary {
|
|
2
|
-
eventType: string;
|
|
3
|
-
currentTool?: string;
|
|
4
|
-
toolCount?: number;
|
|
5
|
-
tokens?: number;
|
|
6
|
-
turns?: number;
|
|
7
|
-
activityState?: string;
|
|
8
|
-
lastActivityAt?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface ProgressEventCoalesceDecision {
|
|
12
|
-
shouldAppend: boolean;
|
|
13
|
-
reason: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ProgressEventCoalesceInput {
|
|
17
|
-
previous?: ProgressEventSummary;
|
|
18
|
-
next: ProgressEventSummary;
|
|
19
|
-
nowMs: number;
|
|
20
|
-
lastAppendMs?: number;
|
|
21
|
-
minIntervalMs: number;
|
|
22
|
-
force?: boolean;
|
|
23
|
-
tokenThreshold?: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const DEFAULT_TOKEN_THRESHOLD = 256;
|
|
27
|
-
|
|
28
|
-
function numericIncrease(previous: number | undefined, next: number | undefined): number {
|
|
29
|
-
return next !== undefined && previous !== undefined ? next - previous : next !== undefined ? next : 0;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function shouldAppendProgressEventUpdate(input: ProgressEventCoalesceInput): ProgressEventCoalesceDecision {
|
|
33
|
-
if (input.force) return { shouldAppend: true, reason: "force" };
|
|
34
|
-
if (!input.previous) return { shouldAppend: true, reason: "first" };
|
|
35
|
-
if (input.previous.activityState !== input.next.activityState) return { shouldAppend: true, reason: "activity_changed" };
|
|
36
|
-
if (input.previous.currentTool !== input.next.currentTool) return { shouldAppend: true, reason: "tool_changed" };
|
|
37
|
-
if (numericIncrease(input.previous.toolCount, input.next.toolCount) > 0) return { shouldAppend: true, reason: "tool_count_increased" };
|
|
38
|
-
if (numericIncrease(input.previous.turns, input.next.turns) > 0) return { shouldAppend: true, reason: "turns_increased" };
|
|
39
|
-
const tokenIncrease = numericIncrease(input.previous.tokens, input.next.tokens);
|
|
40
|
-
if (tokenIncrease >= (input.tokenThreshold ?? DEFAULT_TOKEN_THRESHOLD)) return { shouldAppend: true, reason: "tokens_increased" };
|
|
41
|
-
if (input.lastAppendMs === undefined || input.nowMs - input.lastAppendMs >= input.minIntervalMs) return { shouldAppend: true, reason: "interval" };
|
|
42
|
-
return { shouldAppend: false, reason: "coalesced" };
|
|
43
|
-
}
|
|
1
|
+
export interface ProgressEventSummary {
|
|
2
|
+
eventType: string;
|
|
3
|
+
currentTool?: string;
|
|
4
|
+
toolCount?: number;
|
|
5
|
+
tokens?: number;
|
|
6
|
+
turns?: number;
|
|
7
|
+
activityState?: string;
|
|
8
|
+
lastActivityAt?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ProgressEventCoalesceDecision {
|
|
12
|
+
shouldAppend: boolean;
|
|
13
|
+
reason: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ProgressEventCoalesceInput {
|
|
17
|
+
previous?: ProgressEventSummary;
|
|
18
|
+
next: ProgressEventSummary;
|
|
19
|
+
nowMs: number;
|
|
20
|
+
lastAppendMs?: number;
|
|
21
|
+
minIntervalMs: number;
|
|
22
|
+
force?: boolean;
|
|
23
|
+
tokenThreshold?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DEFAULT_TOKEN_THRESHOLD = 256;
|
|
27
|
+
|
|
28
|
+
function numericIncrease(previous: number | undefined, next: number | undefined): number {
|
|
29
|
+
return next !== undefined && previous !== undefined ? next - previous : next !== undefined ? next : 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function shouldAppendProgressEventUpdate(input: ProgressEventCoalesceInput): ProgressEventCoalesceDecision {
|
|
33
|
+
if (input.force) return { shouldAppend: true, reason: "force" };
|
|
34
|
+
if (!input.previous) return { shouldAppend: true, reason: "first" };
|
|
35
|
+
if (input.previous.activityState !== input.next.activityState) return { shouldAppend: true, reason: "activity_changed" };
|
|
36
|
+
if (input.previous.currentTool !== input.next.currentTool) return { shouldAppend: true, reason: "tool_changed" };
|
|
37
|
+
if (numericIncrease(input.previous.toolCount, input.next.toolCount) > 0) return { shouldAppend: true, reason: "tool_count_increased" };
|
|
38
|
+
if (numericIncrease(input.previous.turns, input.next.turns) > 0) return { shouldAppend: true, reason: "turns_increased" };
|
|
39
|
+
const tokenIncrease = numericIncrease(input.previous.tokens, input.next.tokens);
|
|
40
|
+
if (tokenIncrease >= (input.tokenThreshold ?? DEFAULT_TOKEN_THRESHOLD)) return { shouldAppend: true, reason: "tokens_increased" };
|
|
41
|
+
if (input.lastAppendMs === undefined || input.nowMs - input.lastAppendMs >= input.minIntervalMs) return { shouldAppend: true, reason: "interval" };
|
|
42
|
+
return { shouldAppend: false, reason: "coalesced" };
|
|
43
|
+
}
|
|
@@ -1,164 +1,164 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure TypeScript prose compression module.
|
|
3
|
-
*
|
|
4
|
-
* Inspired by caveman's compress.js — reduces tool descriptions and other
|
|
5
|
-
* prose by removing filler words, pleasantries, hedging, and articles while
|
|
6
|
-
* preserving code, URLs, paths, identifiers, and other protected segments
|
|
7
|
-
* byte-for-byte.
|
|
8
|
-
*
|
|
9
|
-
* No external dependencies. No `any` types. Regex-only pattern matching.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
// Protected segment patterns (order matters — earlier patterns take priority)
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
|
|
16
|
-
const PROTECTED_PATTERNS: readonly RegExp[] = [
|
|
17
|
-
/```[\s\S]*?```/g, // fenced code blocks
|
|
18
|
-
/`[^`\n]+`/g, // inline code
|
|
19
|
-
/\bhttps?:\/\/\S+/gi, // URLs
|
|
20
|
-
/\b[\w.-]*[\/\\][\w.\/\\\-]+/g, // paths with / or \
|
|
21
|
-
/\b[A-Z][A-Z0-9]*(?:_[A-Z][A-Z0-9]*)+\b/g, // CONSTANT_CASE
|
|
22
|
-
/\b\w+(?:\.\w+)+\(\)/g, // dotted.method() calls
|
|
23
|
-
/[A-Za-z_][A-Za-z0-9_]*\s*\([^)]*\)/g, // function calls: name(args)
|
|
24
|
-
/\b\d+\.\d+\.\d+\b/g, // version numbers x.y.z
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Compression patterns (applied to unprotected prose only)
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
const FILLERS = /\b(?:just|really|basically|actually|simply|quite|very|essentially|literally)\b/gi;
|
|
32
|
-
|
|
33
|
-
const PLEASANTRIES = /\b(?:please|kindly|thank you|thanks|sure|certainly|of course|happy to)\b[,.]?\s*/gi;
|
|
34
|
-
|
|
35
|
-
const HEDGES = /\b(?:perhaps|maybe|might|could potentially|would like to|i think|in my opinion)\b\s*/gi;
|
|
36
|
-
|
|
37
|
-
const LEADERS = /^(?:i'?\s*ll|i will|i can|you can|we will|we can|let me|let'?\s*s)\s+/gim;
|
|
38
|
-
|
|
39
|
-
const ARTICLES = /\b(?:a|an|the)\s+(?=[a-z])/gi;
|
|
40
|
-
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
// Sentinel helpers
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
const SENTINEL_OPEN = "\x00";
|
|
46
|
-
const SENTINEL_CLOSE = "\x00";
|
|
47
|
-
const SENTINEL_RE = /\x00(\d+)\x00/g;
|
|
48
|
-
|
|
49
|
-
interface SegmentStore {
|
|
50
|
-
segments: string[];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function extractProtected(text: string, store: SegmentStore): string {
|
|
54
|
-
let working = text;
|
|
55
|
-
for (const re of PROTECTED_PATTERNS) {
|
|
56
|
-
// Reset lastIndex for reused RegExp objects
|
|
57
|
-
re.lastIndex = 0;
|
|
58
|
-
working = working.replace(re, (match: string): string => {
|
|
59
|
-
const index = store.segments.length;
|
|
60
|
-
store.segments.push(match);
|
|
61
|
-
return `${SENTINEL_OPEN}${index}${SENTINEL_CLOSE}`;
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
return working;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function restoreProtected(text: string, store: SegmentStore): string {
|
|
68
|
-
return text.replace(SENTINEL_RE, (_match: string, indexStr: string): string => {
|
|
69
|
-
const index = Number(indexStr);
|
|
70
|
-
return store.segments[index] ?? "";
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
|
-
// Core prose compression
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
|
|
78
|
-
function compressProseText(text: string): string {
|
|
79
|
-
let s = text;
|
|
80
|
-
|
|
81
|
-
// Remove leading "I'll / I will / you can / ..." sentence openers
|
|
82
|
-
s = s.replace(LEADERS, "");
|
|
83
|
-
|
|
84
|
-
// Remove pleasantries
|
|
85
|
-
s = s.replace(PLEASANTRIES, "");
|
|
86
|
-
|
|
87
|
-
// Remove hedging phrases
|
|
88
|
-
s = s.replace(HEDGES, "");
|
|
89
|
-
|
|
90
|
-
// Remove filler adverbs
|
|
91
|
-
s = s.replace(FILLERS, "");
|
|
92
|
-
|
|
93
|
-
// Remove articles before lowercase words
|
|
94
|
-
s = s.replace(ARTICLES, "");
|
|
95
|
-
|
|
96
|
-
// Collapse whitespace introduced by removals
|
|
97
|
-
s = s.replace(/[ \t]{2,}/g, " ");
|
|
98
|
-
|
|
99
|
-
// Fix punctuation spacing (e.g. "word ," → "word,")
|
|
100
|
-
s = s.replace(/\s+([,.;:!?])/g, "$1");
|
|
101
|
-
|
|
102
|
-
// Collapse excessive newlines
|
|
103
|
-
s = s.replace(/\n{3,}/g, "\n\n");
|
|
104
|
-
|
|
105
|
-
// Re-capitalize first letter of each sentence after removals
|
|
106
|
-
s = s.replace(/(^|[.!?]\s+)([a-z])/g, (_match: string, pre: string, ch: string): string => {
|
|
107
|
-
return pre + ch.toUpperCase();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
return s.trim();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
// Public API
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
|
|
117
|
-
export interface CompressResult {
|
|
118
|
-
compressed: string;
|
|
119
|
-
originalLength: number;
|
|
120
|
-
compressedLength: number;
|
|
121
|
-
savingsPercent: number;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Compress prose text by removing filler, pleasantries, hedging, and articles
|
|
126
|
-
* while preserving code blocks, URLs, paths, identifiers, and other protected
|
|
127
|
-
* segments byte-for-byte.
|
|
128
|
-
*/
|
|
129
|
-
export function compressProse(text: string): CompressResult {
|
|
130
|
-
if (typeof text !== "string" || text.length === 0) {
|
|
131
|
-
return {
|
|
132
|
-
compressed: "",
|
|
133
|
-
originalLength: 0,
|
|
134
|
-
compressedLength: 0,
|
|
135
|
-
savingsPercent: 0,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const store: SegmentStore = { segments: [] };
|
|
140
|
-
const extracted = extractProtected(text, store);
|
|
141
|
-
const compressed = compressProseText(extracted);
|
|
142
|
-
const restored = restoreProtected(compressed, store);
|
|
143
|
-
|
|
144
|
-
const originalLength = text.length;
|
|
145
|
-
const compressedLength = restored.length;
|
|
146
|
-
const savingsPercent = originalLength > 0
|
|
147
|
-
? Math.round(((originalLength - compressedLength) / originalLength) * 100 * 100) / 100
|
|
148
|
-
: 0;
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
compressed: restored,
|
|
152
|
-
originalLength,
|
|
153
|
-
compressedLength,
|
|
154
|
-
savingsPercent,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Convenience wrapper — returns just the compressed string for tool
|
|
160
|
-
* descriptions and other single-field use cases.
|
|
161
|
-
*/
|
|
162
|
-
export function compressToolDescription(description: string): string {
|
|
163
|
-
return compressProse(description).compressed;
|
|
164
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Pure TypeScript prose compression module.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by caveman's compress.js — reduces tool descriptions and other
|
|
5
|
+
* prose by removing filler words, pleasantries, hedging, and articles while
|
|
6
|
+
* preserving code, URLs, paths, identifiers, and other protected segments
|
|
7
|
+
* byte-for-byte.
|
|
8
|
+
*
|
|
9
|
+
* No external dependencies. No `any` types. Regex-only pattern matching.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Protected segment patterns (order matters — earlier patterns take priority)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
const PROTECTED_PATTERNS: readonly RegExp[] = [
|
|
17
|
+
/```[\s\S]*?```/g, // fenced code blocks
|
|
18
|
+
/`[^`\n]+`/g, // inline code
|
|
19
|
+
/\bhttps?:\/\/\S+/gi, // URLs
|
|
20
|
+
/\b[\w.-]*[\/\\][\w.\/\\\-]+/g, // paths with / or \
|
|
21
|
+
/\b[A-Z][A-Z0-9]*(?:_[A-Z][A-Z0-9]*)+\b/g, // CONSTANT_CASE
|
|
22
|
+
/\b\w+(?:\.\w+)+\(\)/g, // dotted.method() calls
|
|
23
|
+
/[A-Za-z_][A-Za-z0-9_]*\s*\([^)]*\)/g, // function calls: name(args)
|
|
24
|
+
/\b\d+\.\d+\.\d+\b/g, // version numbers x.y.z
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Compression patterns (applied to unprotected prose only)
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
const FILLERS = /\b(?:just|really|basically|actually|simply|quite|very|essentially|literally)\b/gi;
|
|
32
|
+
|
|
33
|
+
const PLEASANTRIES = /\b(?:please|kindly|thank you|thanks|sure|certainly|of course|happy to)\b[,.]?\s*/gi;
|
|
34
|
+
|
|
35
|
+
const HEDGES = /\b(?:perhaps|maybe|might|could potentially|would like to|i think|in my opinion)\b\s*/gi;
|
|
36
|
+
|
|
37
|
+
const LEADERS = /^(?:i'?\s*ll|i will|i can|you can|we will|we can|let me|let'?\s*s)\s+/gim;
|
|
38
|
+
|
|
39
|
+
const ARTICLES = /\b(?:a|an|the)\s+(?=[a-z])/gi;
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Sentinel helpers
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
const SENTINEL_OPEN = "\x00";
|
|
46
|
+
const SENTINEL_CLOSE = "\x00";
|
|
47
|
+
const SENTINEL_RE = /\x00(\d+)\x00/g;
|
|
48
|
+
|
|
49
|
+
interface SegmentStore {
|
|
50
|
+
segments: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function extractProtected(text: string, store: SegmentStore): string {
|
|
54
|
+
let working = text;
|
|
55
|
+
for (const re of PROTECTED_PATTERNS) {
|
|
56
|
+
// Reset lastIndex for reused RegExp objects
|
|
57
|
+
re.lastIndex = 0;
|
|
58
|
+
working = working.replace(re, (match: string): string => {
|
|
59
|
+
const index = store.segments.length;
|
|
60
|
+
store.segments.push(match);
|
|
61
|
+
return `${SENTINEL_OPEN}${index}${SENTINEL_CLOSE}`;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return working;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function restoreProtected(text: string, store: SegmentStore): string {
|
|
68
|
+
return text.replace(SENTINEL_RE, (_match: string, indexStr: string): string => {
|
|
69
|
+
const index = Number(indexStr);
|
|
70
|
+
return store.segments[index] ?? "";
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Core prose compression
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
function compressProseText(text: string): string {
|
|
79
|
+
let s = text;
|
|
80
|
+
|
|
81
|
+
// Remove leading "I'll / I will / you can / ..." sentence openers
|
|
82
|
+
s = s.replace(LEADERS, "");
|
|
83
|
+
|
|
84
|
+
// Remove pleasantries
|
|
85
|
+
s = s.replace(PLEASANTRIES, "");
|
|
86
|
+
|
|
87
|
+
// Remove hedging phrases
|
|
88
|
+
s = s.replace(HEDGES, "");
|
|
89
|
+
|
|
90
|
+
// Remove filler adverbs
|
|
91
|
+
s = s.replace(FILLERS, "");
|
|
92
|
+
|
|
93
|
+
// Remove articles before lowercase words
|
|
94
|
+
s = s.replace(ARTICLES, "");
|
|
95
|
+
|
|
96
|
+
// Collapse whitespace introduced by removals
|
|
97
|
+
s = s.replace(/[ \t]{2,}/g, " ");
|
|
98
|
+
|
|
99
|
+
// Fix punctuation spacing (e.g. "word ," → "word,")
|
|
100
|
+
s = s.replace(/\s+([,.;:!?])/g, "$1");
|
|
101
|
+
|
|
102
|
+
// Collapse excessive newlines
|
|
103
|
+
s = s.replace(/\n{3,}/g, "\n\n");
|
|
104
|
+
|
|
105
|
+
// Re-capitalize first letter of each sentence after removals
|
|
106
|
+
s = s.replace(/(^|[.!?]\s+)([a-z])/g, (_match: string, pre: string, ch: string): string => {
|
|
107
|
+
return pre + ch.toUpperCase();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return s.trim();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Public API
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
export interface CompressResult {
|
|
118
|
+
compressed: string;
|
|
119
|
+
originalLength: number;
|
|
120
|
+
compressedLength: number;
|
|
121
|
+
savingsPercent: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Compress prose text by removing filler, pleasantries, hedging, and articles
|
|
126
|
+
* while preserving code blocks, URLs, paths, identifiers, and other protected
|
|
127
|
+
* segments byte-for-byte.
|
|
128
|
+
*/
|
|
129
|
+
export function compressProse(text: string): CompressResult {
|
|
130
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
131
|
+
return {
|
|
132
|
+
compressed: "",
|
|
133
|
+
originalLength: 0,
|
|
134
|
+
compressedLength: 0,
|
|
135
|
+
savingsPercent: 0,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const store: SegmentStore = { segments: [] };
|
|
140
|
+
const extracted = extractProtected(text, store);
|
|
141
|
+
const compressed = compressProseText(extracted);
|
|
142
|
+
const restored = restoreProtected(compressed, store);
|
|
143
|
+
|
|
144
|
+
const originalLength = text.length;
|
|
145
|
+
const compressedLength = restored.length;
|
|
146
|
+
const savingsPercent = originalLength > 0
|
|
147
|
+
? Math.round(((originalLength - compressedLength) / originalLength) * 100 * 100) / 100
|
|
148
|
+
: 0;
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
compressed: restored,
|
|
152
|
+
originalLength,
|
|
153
|
+
compressedLength,
|
|
154
|
+
savingsPercent,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Convenience wrapper — returns just the compressed string for tool
|
|
160
|
+
* descriptions and other single-field use cases.
|
|
161
|
+
*/
|
|
162
|
+
export function compressToolDescription(description: string): string {
|
|
163
|
+
return compressProse(description).compressed;
|
|
164
|
+
}
|
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
import type { PolicyDecision, PolicyDecisionReason } from "../state/types.ts";
|
|
2
|
-
|
|
3
|
-
export type FailureScenario = "trust_prompt_unresolved" | "prompt_misdelivery" | "stale_branch" | "compile_red_cross_crate" | "mcp_handshake_failure" | "partial_plugin_startup" | "provider_failure" | "task_failed" | "worker_stale" | "green_unsatisfied";
|
|
4
|
-
export type RecoveryStep = "accept_trust_prompt" | "redirect_prompt_to_agent" | "rebase_branch" | "clean_build" | "retry_mcp_handshake" | "restart_plugin" | "restart_worker" | "rerun_task" | "collect_verification_evidence" | "escalate_to_human";
|
|
5
|
-
export type RecoveryResultState = "planned" | "skipped" | "escalation_required";
|
|
6
|
-
|
|
7
|
-
export interface RecoveryRecipe {
|
|
8
|
-
scenario: FailureScenario;
|
|
9
|
-
steps: RecoveryStep[];
|
|
10
|
-
maxAttempts: number;
|
|
11
|
-
escalationPolicy: "alert_human" | "log_and_continue" | "abort";
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface RecoveryLedgerEntry {
|
|
15
|
-
scenario: FailureScenario;
|
|
16
|
-
taskId?: string;
|
|
17
|
-
decisionReason: PolicyDecisionReason;
|
|
18
|
-
attempt: number;
|
|
19
|
-
state: RecoveryResultState;
|
|
20
|
-
steps: RecoveryStep[];
|
|
21
|
-
message: string;
|
|
22
|
-
createdAt: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface RecoveryLedger {
|
|
26
|
-
entries: RecoveryLedgerEntry[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function scenarioForPolicyReason(reason: PolicyDecisionReason): FailureScenario {
|
|
30
|
-
switch (reason) {
|
|
31
|
-
case "branch_stale": return "stale_branch";
|
|
32
|
-
case "worker_stale": return "worker_stale";
|
|
33
|
-
case "green_unsatisfied": return "green_unsatisfied";
|
|
34
|
-
case "task_failed": return "task_failed";
|
|
35
|
-
default: return "provider_failure";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function recipeFor(scenario: FailureScenario): RecoveryRecipe {
|
|
40
|
-
switch (scenario) {
|
|
41
|
-
case "trust_prompt_unresolved": return { scenario, steps: ["accept_trust_prompt"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
42
|
-
case "prompt_misdelivery": return { scenario, steps: ["redirect_prompt_to_agent"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
43
|
-
case "stale_branch": return { scenario, steps: ["rebase_branch", "clean_build"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
44
|
-
case "compile_red_cross_crate": return { scenario, steps: ["clean_build"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
45
|
-
case "mcp_handshake_failure": return { scenario, steps: ["retry_mcp_handshake"], maxAttempts: 1, escalationPolicy: "abort" };
|
|
46
|
-
case "partial_plugin_startup": return { scenario, steps: ["restart_plugin", "retry_mcp_handshake"], maxAttempts: 1, escalationPolicy: "log_and_continue" };
|
|
47
|
-
case "worker_stale": return { scenario, steps: ["restart_worker"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
48
|
-
case "green_unsatisfied": return { scenario, steps: ["collect_verification_evidence"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
49
|
-
case "task_failed": return { scenario, steps: ["rerun_task"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
50
|
-
case "provider_failure": return { scenario, steps: ["restart_worker"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function buildRecoveryLedger(decisions: PolicyDecision[], previous: RecoveryLedger = { entries: [] }): RecoveryLedger {
|
|
55
|
-
const entries = [...previous.entries];
|
|
56
|
-
for (const item of decisions) {
|
|
57
|
-
if (!["retry", "escalate", "block"].includes(item.action)) continue;
|
|
58
|
-
const scenario = scenarioForPolicyReason(item.reason);
|
|
59
|
-
const recipe = recipeFor(scenario);
|
|
60
|
-
const priorAttempts = entries.filter((entry) => entry.scenario === scenario && entry.taskId === item.taskId).length;
|
|
61
|
-
const attempt = priorAttempts + 1;
|
|
62
|
-
entries.push({
|
|
63
|
-
scenario,
|
|
64
|
-
taskId: item.taskId,
|
|
65
|
-
decisionReason: item.reason,
|
|
66
|
-
attempt,
|
|
67
|
-
state: attempt <= recipe.maxAttempts && item.action !== "block" ? "planned" : "escalation_required",
|
|
68
|
-
steps: attempt <= recipe.maxAttempts ? recipe.steps : ["escalate_to_human"],
|
|
69
|
-
message: item.message,
|
|
70
|
-
createdAt: new Date().toISOString(),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
return { entries };
|
|
74
|
-
}
|
|
1
|
+
import type { PolicyDecision, PolicyDecisionReason } from "../state/types.ts";
|
|
2
|
+
|
|
3
|
+
export type FailureScenario = "trust_prompt_unresolved" | "prompt_misdelivery" | "stale_branch" | "compile_red_cross_crate" | "mcp_handshake_failure" | "partial_plugin_startup" | "provider_failure" | "task_failed" | "worker_stale" | "green_unsatisfied";
|
|
4
|
+
export type RecoveryStep = "accept_trust_prompt" | "redirect_prompt_to_agent" | "rebase_branch" | "clean_build" | "retry_mcp_handshake" | "restart_plugin" | "restart_worker" | "rerun_task" | "collect_verification_evidence" | "escalate_to_human";
|
|
5
|
+
export type RecoveryResultState = "planned" | "skipped" | "escalation_required";
|
|
6
|
+
|
|
7
|
+
export interface RecoveryRecipe {
|
|
8
|
+
scenario: FailureScenario;
|
|
9
|
+
steps: RecoveryStep[];
|
|
10
|
+
maxAttempts: number;
|
|
11
|
+
escalationPolicy: "alert_human" | "log_and_continue" | "abort";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RecoveryLedgerEntry {
|
|
15
|
+
scenario: FailureScenario;
|
|
16
|
+
taskId?: string;
|
|
17
|
+
decisionReason: PolicyDecisionReason;
|
|
18
|
+
attempt: number;
|
|
19
|
+
state: RecoveryResultState;
|
|
20
|
+
steps: RecoveryStep[];
|
|
21
|
+
message: string;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface RecoveryLedger {
|
|
26
|
+
entries: RecoveryLedgerEntry[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function scenarioForPolicyReason(reason: PolicyDecisionReason): FailureScenario {
|
|
30
|
+
switch (reason) {
|
|
31
|
+
case "branch_stale": return "stale_branch";
|
|
32
|
+
case "worker_stale": return "worker_stale";
|
|
33
|
+
case "green_unsatisfied": return "green_unsatisfied";
|
|
34
|
+
case "task_failed": return "task_failed";
|
|
35
|
+
default: return "provider_failure";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function recipeFor(scenario: FailureScenario): RecoveryRecipe {
|
|
40
|
+
switch (scenario) {
|
|
41
|
+
case "trust_prompt_unresolved": return { scenario, steps: ["accept_trust_prompt"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
42
|
+
case "prompt_misdelivery": return { scenario, steps: ["redirect_prompt_to_agent"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
43
|
+
case "stale_branch": return { scenario, steps: ["rebase_branch", "clean_build"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
44
|
+
case "compile_red_cross_crate": return { scenario, steps: ["clean_build"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
45
|
+
case "mcp_handshake_failure": return { scenario, steps: ["retry_mcp_handshake"], maxAttempts: 1, escalationPolicy: "abort" };
|
|
46
|
+
case "partial_plugin_startup": return { scenario, steps: ["restart_plugin", "retry_mcp_handshake"], maxAttempts: 1, escalationPolicy: "log_and_continue" };
|
|
47
|
+
case "worker_stale": return { scenario, steps: ["restart_worker"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
48
|
+
case "green_unsatisfied": return { scenario, steps: ["collect_verification_evidence"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
49
|
+
case "task_failed": return { scenario, steps: ["rerun_task"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
50
|
+
case "provider_failure": return { scenario, steps: ["restart_worker"], maxAttempts: 1, escalationPolicy: "alert_human" };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function buildRecoveryLedger(decisions: PolicyDecision[], previous: RecoveryLedger = { entries: [] }): RecoveryLedger {
|
|
55
|
+
const entries = [...previous.entries];
|
|
56
|
+
for (const item of decisions) {
|
|
57
|
+
if (!["retry", "escalate", "block"].includes(item.action)) continue;
|
|
58
|
+
const scenario = scenarioForPolicyReason(item.reason);
|
|
59
|
+
const recipe = recipeFor(scenario);
|
|
60
|
+
const priorAttempts = entries.filter((entry) => entry.scenario === scenario && entry.taskId === item.taskId).length;
|
|
61
|
+
const attempt = priorAttempts + 1;
|
|
62
|
+
entries.push({
|
|
63
|
+
scenario,
|
|
64
|
+
taskId: item.taskId,
|
|
65
|
+
decisionReason: item.reason,
|
|
66
|
+
attempt,
|
|
67
|
+
state: attempt <= recipe.maxAttempts && item.action !== "block" ? "planned" : "escalation_required",
|
|
68
|
+
steps: attempt <= recipe.maxAttempts ? recipe.steps : ["escalate_to_human"],
|
|
69
|
+
message: item.message,
|
|
70
|
+
createdAt: new Date().toISOString(),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return { entries };
|
|
74
|
+
}
|