pi-crew 0.2.3 → 0.2.5
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/AGENTS.md +57 -32
- package/CHANGELOG.md +466 -448
- package/LICENSE +21 -21
- package/NOTICE.md +16 -16
- package/README.md +323 -323
- package/docs/FEATURE_INTAKE.md +126 -0
- package/docs/HARNESS.md +86 -0
- package/docs/HARNESS_BACKLOG.md +41 -0
- package/docs/TEST_MATRIX.md +49 -0
- package/docs/actions-reference.md +595 -595
- package/docs/architecture.md +180 -180
- package/docs/code-review-2026-05-11.md +592 -592
- package/docs/commands-reference.md +347 -347
- package/docs/comparison-pi-subagents-vs-pi-crew.md +303 -0
- package/docs/decisions/0001-durable-state.md +41 -0
- package/docs/decisions/0002-child-process-for-async.md +42 -0
- package/docs/decisions/0003-depth-guard.md +36 -0
- package/docs/decisions/0004-execfile-over-exec.md +34 -0
- package/docs/decisions/0005-no-parameter-properties.md +49 -0
- package/docs/decisions/0006-publish-bundled-esm.md +63 -0
- package/docs/decisions/0007-active-run-binary-index.md +54 -0
- package/docs/decisions/0008-child-pi-warm-pool.md +61 -0
- package/docs/decisions/README.md +23 -0
- package/docs/followup-review-round4-2026-05-13.md +107 -0
- package/docs/implementation-plan-top3.md +333 -0
- package/docs/live-mailbox-runtime.md +36 -36
- package/docs/next-upgrade-roadmap.md +808 -808
- package/docs/oh-my-pi-research.md +509 -0
- package/docs/perf/baseline-2026-05.md +113 -0
- package/docs/perf/final-report-2026-05.md +206 -0
- package/docs/perf/sprint-1-report.md +71 -0
- package/docs/perf/sprint-2-report.md +81 -0
- package/docs/perf/sprint-2.5-report.md +53 -0
- package/docs/perf/sprint-3-report.md +36 -0
- package/docs/perf/sprint-4-report.md +47 -0
- package/docs/perf/sprint-5-report.md +51 -0
- package/docs/perf/sprint-6-report.md +94 -0
- package/docs/perf/sprint-7-report.md +74 -0
- package/docs/perf/upgrade-plan-2026-05.md +147 -0
- package/docs/pi-subagents3-deep-analysis.md +508 -0
- package/docs/product/README.md +31 -0
- package/docs/product/platform.md +27 -0
- package/docs/product/runtime-safety.md +37 -0
- package/docs/product/team-run.md +39 -0
- package/docs/product/team-tool.md +37 -0
- package/docs/publishing.md +65 -65
- package/docs/resource-formats.md +134 -134
- package/docs/runtime-analysis-child-vs-live.md +171 -0
- package/docs/runtime-flow.md +148 -148
- package/docs/runtime-migration-in-process-analysis.md +250 -0
- package/docs/stories/README.md +30 -0
- package/docs/stories/backlog.md +36 -0
- package/docs/templates/decision.md +27 -0
- package/docs/templates/story.md +44 -0
- package/docs/templates/validation-report.md +32 -0
- package/docs/usage.md +238 -238
- package/index.ts +7 -6
- package/install.mjs +65 -65
- package/package.json +107 -100
- package/schema.json +222 -222
- package/skills/child-pi-spawning/SKILL.md +213 -0
- package/skills/context-artifact-hygiene/SKILL.md +32 -0
- package/skills/event-log-tracing/SKILL.md +299 -0
- package/skills/git-master/SKILL.md +225 -24
- package/skills/live-agent-lifecycle/SKILL.md +192 -0
- package/skills/mailbox-interactive/SKILL.md +300 -19
- package/skills/model-routing-context/SKILL.md +94 -0
- package/skills/multi-perspective-review/SKILL.md +88 -0
- package/skills/read-only-explorer/SKILL.md +250 -26
- package/skills/safe-bash/SKILL.md +307 -21
- package/skills/verification-before-done/SKILL.md +11 -2
- package/skills/widget-rendering/SKILL.md +258 -0
- package/skills/workspace-isolation/SKILL.md +202 -0
- package/skills/worktree-isolation/SKILL.md +202 -18
- package/src/adapters/claude-adapter.ts +25 -25
- package/src/adapters/codex-adapter.ts +21 -21
- package/src/adapters/cursor-adapter.ts +17 -17
- package/src/adapters/export-util.ts +137 -137
- package/src/adapters/index.ts +15 -15
- package/src/adapters/registry.ts +18 -18
- package/src/adapters/types.ts +23 -23
- package/src/agents/agent-config.ts +38 -38
- package/src/agents/agent-serializer.ts +38 -38
- package/src/agents/discover-agents.ts +121 -118
- package/src/config/config.ts +740 -858
- package/src/config/defaults.ts +96 -96
- package/src/config/drift-detector.ts +211 -211
- package/src/config/markers.ts +327 -327
- package/src/config/resilient-parser.ts +109 -108
- package/src/config/suggestions.ts +74 -74
- package/src/config/types.ts +199 -0
- package/src/extension/async-notifier.ts +123 -89
- package/src/extension/autonomous-policy.ts +169 -169
- package/src/extension/cross-extension-rpc.ts +104 -104
- package/src/extension/help.ts +47 -47
- package/src/extension/import-index.ts +69 -69
- package/src/extension/management.ts +395 -382
- package/src/extension/notification-router.ts +116 -116
- package/src/extension/notification-sink.ts +51 -51
- package/src/extension/project-init.ts +168 -168
- package/src/extension/register.ts +859 -668
- package/src/extension/registration/artifact-cleanup.ts +15 -15
- package/src/extension/registration/command-utils.ts +54 -54
- package/src/extension/registration/commands.ts +559 -452
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-helpers.ts +102 -102
- package/src/extension/registration/subagent-tools.ts +220 -159
- package/src/extension/registration/team-tool.ts +159 -99
- package/src/extension/registration/viewers.ts +29 -0
- package/src/extension/result-watcher.ts +128 -128
- package/src/extension/run-bundle-schema.ts +89 -89
- package/src/extension/run-export.ts +73 -73
- package/src/extension/run-import.ts +84 -84
- package/src/extension/run-index.ts +94 -94
- package/src/extension/run-maintenance.ts +142 -142
- package/src/extension/session-summary.ts +8 -8
- package/src/extension/team-manager-command.ts +96 -96
- package/src/extension/team-recommendation.ts +188 -188
- package/src/extension/team-tool/api.ts +5 -2
- package/src/extension/team-tool/cancel.ts +224 -209
- package/src/extension/team-tool/config-patch.ts +36 -36
- package/src/extension/team-tool/context.ts +60 -60
- package/src/extension/team-tool/doctor.ts +242 -242
- package/src/extension/team-tool/handle-settings.ts +421 -195
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +139 -139
- package/src/extension/team-tool/parallel-dispatch.ts +156 -156
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/respond.ts +112 -111
- package/src/extension/team-tool/run.ts +246 -229
- package/src/extension/team-tool/status.ts +110 -110
- package/src/extension/team-tool-types.ts +13 -13
- package/src/extension/team-tool.ts +344 -344
- package/src/extension/tool-result.ts +16 -16
- package/src/extension/validate-resources.ts +77 -77
- package/src/hooks/registry.ts +61 -61
- package/src/hooks/types.ts +40 -40
- package/src/i18n.ts +184 -184
- package/src/observability/correlation.ts +35 -35
- package/src/observability/event-to-metric.ts +68 -68
- package/src/observability/exporters/adapter.ts +30 -30
- package/src/observability/exporters/otlp-exporter.ts +106 -92
- package/src/observability/exporters/prometheus-exporter.ts +54 -54
- package/src/observability/metric-registry.ts +87 -87
- package/src/observability/metric-retention.ts +54 -54
- package/src/observability/metric-sink.ts +81 -56
- package/src/observability/metrics-primitives.ts +167 -167
- package/src/prompt/prompt-runtime.ts +72 -72
- package/src/runtime/adaptive-plan.ts +338 -0
- package/src/runtime/agent-control.ts +169 -169
- 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/async-runner.ts +153 -153
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/auto-resume.ts +100 -100
- package/src/runtime/background-runner.ts +122 -89
- package/src/runtime/cancellation.ts +61 -61
- package/src/runtime/capability-inventory.ts +116 -116
- package/src/runtime/child-pi-pool.ts +68 -0
- package/src/runtime/child-pi.ts +541 -461
- package/src/runtime/code-summary.ts +247 -247
- package/src/runtime/compaction-summary.ts +271 -271
- package/src/runtime/concurrency.ts +58 -58
- package/src/runtime/crash-recovery.ts +317 -301
- package/src/runtime/crew-agent-records.ts +379 -281
- package/src/runtime/crew-agent-runtime.ts +60 -60
- package/src/runtime/cross-extension-rpc.ts +72 -0
- package/src/runtime/custom-tools/irc-tool.ts +201 -201
- package/src/runtime/custom-tools/submit-result-tool.ts +90 -90
- package/src/runtime/deadletter.ts +47 -47
- package/src/runtime/delivery-coordinator.ts +176 -176
- package/src/runtime/delta-conflict.ts +360 -360
- package/src/runtime/diagnostic-export.ts +102 -102
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/effectiveness.ts +82 -81
- package/src/runtime/errors/crew-errors.ts +166 -0
- package/src/runtime/event-stream-bridge.ts +92 -92
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +234 -106
- package/src/runtime/heartbeat-watcher.ts +145 -124
- package/src/runtime/iteration-hooks.ts +267 -267
- package/src/runtime/live-agent-control.ts +88 -88
- package/src/runtime/live-agent-manager.ts +377 -179
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +676 -600
- package/src/runtime/loop-gates.ts +129 -129
- package/src/runtime/manifest-cache.ts +263 -263
- package/src/runtime/mcp-proxy.ts +113 -113
- package/src/runtime/metric-parser.ts +40 -40
- package/src/runtime/model-fallback.ts +282 -274
- package/src/runtime/model-resolver.ts +118 -0
- package/src/runtime/output-validator.ts +187 -187
- package/src/runtime/overflow-recovery.ts +175 -175
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +156 -156
- package/src/runtime/parent-guard.ts +80 -80
- package/src/runtime/phase-progress.ts +217 -217
- package/src/runtime/pi-args.ts +165 -165
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/pi-spawn.ts +167 -167
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/post-checks.ts +125 -125
- package/src/runtime/post-exit-stdio-guard.ts +86 -86
- package/src/runtime/process-status.ts +97 -73
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +81 -81
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/run-tracker.ts +99 -0
- package/src/runtime/runtime-policy.ts +21 -0
- package/src/runtime/runtime-resolver.ts +94 -91
- package/src/runtime/scheduler.ts +294 -0
- package/src/runtime/semaphore.ts +131 -131
- package/src/runtime/sensitive-paths.ts +92 -92
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/settings-store.ts +103 -0
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/skill-instructions.ts +222 -222
- package/src/runtime/stale-reconciler.ts +198 -189
- package/src/runtime/streaming-output.ts +47 -0
- package/src/runtime/subagent-manager.ts +404 -400
- package/src/runtime/subprocess-tool-registry.ts +67 -67
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-graph-scheduler.ts +122 -122
- package/src/runtime/task-graph.ts +207 -207
- package/src/runtime/task-output-context.ts +177 -177
- package/src/runtime/task-packet.ts +93 -93
- package/src/runtime/task-quality.ts +207 -207
- package/src/runtime/task-runner/capabilities.ts +78 -78
- package/src/runtime/task-runner/live-executor.ts +131 -113
- package/src/runtime/task-runner/progress.ts +119 -119
- package/src/runtime/task-runner/prompt-builder.ts +139 -139
- 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/task-runner.ts +469 -459
- package/src/runtime/team-runner.ts +693 -945
- package/src/runtime/usage-tracker.ts +71 -0
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/runtime/workflow-state.ts +187 -187
- package/src/runtime/yield-handler.ts +190 -190
- package/src/schema/config-schema.ts +172 -168
- package/src/schema/team-tool-schema.ts +126 -126
- package/src/schema/validation-types.ts +151 -148
- package/src/skills/discover-skills.ts +67 -67
- package/src/skills/skill-templates.ts +374 -374
- package/src/state/active-run-registry.ts +227 -191
- package/src/state/artifact-store.ts +130 -129
- package/src/state/atomic-write.ts +262 -195
- package/src/state/blob-store.ts +116 -116
- package/src/state/contracts.ts +111 -111
- package/src/state/event-log-rotation.ts +161 -158
- package/src/state/event-log.ts +383 -303
- package/src/state/event-reconstructor.ts +217 -217
- package/src/state/jsonl-writer.ts +82 -82
- package/src/state/locks.ts +146 -146
- package/src/state/mailbox.ts +446 -405
- package/src/state/state-store.ts +364 -351
- package/src/state/task-claims.ts +44 -44
- package/src/state/types.ts +285 -285
- 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/discover-teams.ts +116 -116
- package/src/teams/team-config.ts +27 -27
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/agent-management-overlay.ts +144 -144
- package/src/ui/crew-widget.ts +487 -370
- package/src/ui/dashboard-panes/agents-pane.ts +109 -28
- 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/health-pane.ts +30 -30
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -35
- package/src/ui/dashboard-panes/progress-pane.ts +30 -30
- package/src/ui/dashboard-panes/transcript-pane.ts +10 -10
- package/src/ui/heartbeat-aggregator.ts +63 -63
- package/src/ui/keybinding-map.ts +97 -94
- package/src/ui/live-conversation-overlay.ts +152 -0
- package/src/ui/live-run-sidebar.ts +180 -180
- package/src/ui/mascot.ts +442 -442
- package/src/ui/overlays/agent-picker-overlay.ts +57 -57
- package/src/ui/overlays/confirm-overlay.ts +58 -58
- package/src/ui/overlays/mailbox-compose-overlay.ts +144 -144
- package/src/ui/overlays/mailbox-compose-preview.ts +63 -63
- package/src/ui/overlays/mailbox-detail-overlay.ts +122 -122
- package/src/ui/pi-ui-compat.ts +57 -57
- package/src/ui/powerbar-publisher.ts +221 -197
- package/src/ui/render-scheduler.ts +216 -143
- package/src/ui/run-action-dispatcher.ts +118 -118
- package/src/ui/run-dashboard.ts +526 -464
- package/src/ui/run-event-bus.ts +208 -208
- package/src/ui/run-snapshot-cache.ts +826 -777
- package/src/ui/settings-overlay.ts +721 -0
- package/src/ui/snapshot-types.ts +86 -70
- package/src/ui/theme-adapter.ts +190 -190
- package/src/ui/tool-progress-formatter.ts +89 -0
- package/src/ui/transcript-cache.ts +94 -94
- package/src/ui/transcript-viewer.ts +335 -335
- package/src/utils/conflict-detect.ts +662 -0
- package/src/utils/file-coalescer.ts +86 -86
- package/src/utils/frontmatter.ts +68 -68
- package/src/utils/fs-watch.ts +88 -31
- package/src/utils/gh-protocol.ts +479 -0
- package/src/utils/ids.ts +17 -17
- package/src/utils/incremental-reader.ts +104 -104
- package/src/utils/internal-error.ts +6 -6
- package/src/utils/names.ts +27 -27
- package/src/utils/paths.ts +102 -63
- 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/sse-parser.ts +134 -134
- package/src/utils/task-name-generator.ts +337 -337
- package/src/utils/timings.ts +33 -33
- package/src/utils/visual.ts +243 -198
- package/src/workflows/discover-workflows.ts +139 -139
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/workflows/workflow-config.ts +26 -26
- package/src/workflows/workflow-serializer.ts +32 -32
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/cleanup.ts +75 -75
- package/src/worktree/worktree-manager.ts +188 -188
- 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/tsconfig.json +19 -19
- package/workflows/default.workflow.md +30 -30
- package/workflows/fast-fix.workflow.md +23 -23
- package/workflows/implementation.workflow.md +43 -43
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
- package/skills/task-packet/SKILL.md +0 -28
- package/skills/verify-evidence/SKILL.md +0 -27
|
@@ -1,190 +1,190 @@
|
|
|
1
|
-
import { subprocessToolRegistry, type SubprocessToolEvent } from "./subprocess-tool-registry.ts";
|
|
2
|
-
|
|
3
|
-
// G3: Full AJV-based schema validation for yield data.
|
|
4
|
-
// Falls back to lightweight validation if AJV is unavailable.
|
|
5
|
-
|
|
6
|
-
let _ajv: Awaited<ReturnType<typeof getAjvInternal>> | null | undefined;
|
|
7
|
-
|
|
8
|
-
async function getAjvInternal() {
|
|
9
|
-
// LAZY: AJV is an optional heavy validator — only load on first use.
|
|
10
|
-
const mod = await import("ajv");
|
|
11
|
-
const AjvCtor = ("default" in mod ? (mod as Record<string, unknown>).default : mod) as unknown as new (opts: Record<string, unknown>) => { compile: (schema: unknown) => { (data: unknown): boolean; errors?: unknown[] }; errorsText: (errors?: unknown[]) => string };
|
|
12
|
-
return new AjvCtor({ allErrors: true, strict: false, logger: false });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function getAjv() {
|
|
16
|
-
if (_ajv === undefined) {
|
|
17
|
-
try {
|
|
18
|
-
_ajv = await getAjvInternal();
|
|
19
|
-
} catch {
|
|
20
|
-
_ajv = null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return _ajv;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface SchemaValidationResult {
|
|
27
|
-
valid: boolean;
|
|
28
|
-
error?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Validate yield data against a JSON Schema.
|
|
33
|
-
* Uses AJV when available (hoisted dep from pi-ai), falls back to
|
|
34
|
-
* lightweight type checking when not.
|
|
35
|
-
*/
|
|
36
|
-
export async function validateYieldData(data: unknown, schema: unknown): Promise<SchemaValidationResult> {
|
|
37
|
-
if (!schema) return { valid: true };
|
|
38
|
-
if (data === undefined || data === null) return { valid: false, error: "Yield data is null or undefined." };
|
|
39
|
-
|
|
40
|
-
// Try AJV first
|
|
41
|
-
const ajv = await getAjv();
|
|
42
|
-
if (ajv) {
|
|
43
|
-
try {
|
|
44
|
-
const validate = ajv.compile(schema as Record<string, unknown>);
|
|
45
|
-
const valid = validate(data);
|
|
46
|
-
if (!valid) {
|
|
47
|
-
return { valid: false, error: ajv.errorsText(validate.errors ?? undefined) };
|
|
48
|
-
}
|
|
49
|
-
return { valid: true };
|
|
50
|
-
} catch {
|
|
51
|
-
// AJV compile failed — fall through to lightweight
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Lightweight fallback
|
|
56
|
-
return lightweightValidate(data, schema);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function lightweightValidate(data: unknown, schema: unknown): SchemaValidationResult {
|
|
60
|
-
if (typeof schema === "boolean") return { valid: schema };
|
|
61
|
-
if (typeof schema !== "object" || Array.isArray(schema)) return { valid: true };
|
|
62
|
-
const schemaObj = schema as Record<string, unknown>;
|
|
63
|
-
|
|
64
|
-
// Check type constraint
|
|
65
|
-
if (typeof schemaObj.type === "string") {
|
|
66
|
-
const expected = schemaObj.type;
|
|
67
|
-
const actual = Array.isArray(data) ? "array" : typeof data;
|
|
68
|
-
if (expected === "object" && (typeof data !== "object" || Array.isArray(data))) return { valid: false, error: `Expected type '${expected}' but got '${actual}'.` };
|
|
69
|
-
if (expected === "array" && !Array.isArray(data)) return { valid: false, error: `Expected type 'array' but got '${actual}'.` };
|
|
70
|
-
if (expected === "string" && typeof data !== "string") return { valid: false, error: `Expected type 'string' but got '${actual}'.` };
|
|
71
|
-
if (expected === "number" && typeof data !== "number") return { valid: false, error: `Expected type 'number' but got '${actual}'.` };
|
|
72
|
-
if (expected === "boolean" && typeof data !== "boolean") return { valid: false, error: `Expected type 'boolean' but got '${actual}'.` };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Check required fields
|
|
76
|
-
if (Array.isArray(schemaObj.required) && typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
77
|
-
const dataObj = data as Record<string, unknown>;
|
|
78
|
-
for (const field of schemaObj.required) {
|
|
79
|
-
if (typeof field === "string" && !(field in dataObj)) {
|
|
80
|
-
return { valid: false, error: `Missing required field '${field}'.` };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return { valid: true };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface YieldResult {
|
|
89
|
-
summary: string;
|
|
90
|
-
artifacts?: Record<string, string>;
|
|
91
|
-
structuredData?: Record<string, unknown>;
|
|
92
|
-
toolCallId: string;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface YieldConfig {
|
|
96
|
-
enabled: boolean;
|
|
97
|
-
maxReminders: number;
|
|
98
|
-
reminderPrompt: string;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const DEFAULT_YIELD_CONFIG: YieldConfig = {
|
|
102
|
-
enabled: true,
|
|
103
|
-
maxReminders: 3,
|
|
104
|
-
reminderPrompt: "You must call the submit_result tool to return your results.",
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/** Tool name used by workers to yield their result. */
|
|
108
|
-
export const YIELD_TOOL_NAME = "submit_result";
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Check if a value is a plain object record (non-null, non-array object).
|
|
112
|
-
*/
|
|
113
|
-
function isObjectRecord(value: unknown): value is Record<string, unknown> {
|
|
114
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Check if a value is a record with all string values.
|
|
119
|
-
*/
|
|
120
|
-
function isStringRecord(value: unknown): value is Record<string, string> {
|
|
121
|
-
if (!isObjectRecord(value)) return false;
|
|
122
|
-
return Object.values(value).every((v) => typeof v === "string");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Shared helper to extract yield data from tool call arguments.
|
|
127
|
-
* Used by both `extractYieldResult` and `registerYieldTool.extractData`
|
|
128
|
-
* to avoid duplicating parsing logic.
|
|
129
|
-
*/
|
|
130
|
-
export function extractYieldDataFromArgs(args: unknown, toolCallId: string): YieldResult | undefined {
|
|
131
|
-
if (!isObjectRecord(args)) return undefined;
|
|
132
|
-
const summary = typeof args.summary === "string" ? args.summary : "";
|
|
133
|
-
if (!summary) return undefined;
|
|
134
|
-
const result: YieldResult = { summary, toolCallId };
|
|
135
|
-
if (args.artifacts && isStringRecord(args.artifacts)) {
|
|
136
|
-
result.artifacts = args.artifacts;
|
|
137
|
-
}
|
|
138
|
-
if (args.structuredData && isObjectRecord(args.structuredData)) {
|
|
139
|
-
result.structuredData = args.structuredData;
|
|
140
|
-
}
|
|
141
|
-
return result;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Check if a JSON event represents a yield/submit_result tool call.
|
|
146
|
-
* Supports event types: tool_execution_start, toolCall, tool_call.
|
|
147
|
-
*/
|
|
148
|
-
export function isYieldEvent(event: Record<string, unknown>): boolean {
|
|
149
|
-
const type = event.type;
|
|
150
|
-
if (type !== "tool_execution_start" && type !== "toolCall" && type !== "tool_call") return false;
|
|
151
|
-
const toolName = event.toolName ?? event.name ?? event.tool;
|
|
152
|
-
return toolName === YIELD_TOOL_NAME;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Extract structured result from a yield event.
|
|
157
|
-
*/
|
|
158
|
-
export function extractYieldResult(event: Record<string, unknown>): YieldResult | undefined {
|
|
159
|
-
if (!isYieldEvent(event)) return undefined;
|
|
160
|
-
const toolCallId = typeof event.toolCallId === "string" ? event.toolCallId : "";
|
|
161
|
-
return extractYieldDataFromArgs(event.args, toolCallId);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Check if a worker output sequence contains a yield.
|
|
166
|
-
*/
|
|
167
|
-
export function hasYieldInOutput(events: Record<string, unknown>[]): boolean {
|
|
168
|
-
return events.some((event) => isYieldEvent(event));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Build a reminder prompt for workers that haven't yielded.
|
|
173
|
-
*/
|
|
174
|
-
export function buildYieldReminder(attempt: number, maxAttempts: number, reminderPrompt?: string): string {
|
|
175
|
-
return `[Yield Reminder ${attempt}/${maxAttempts}] ${reminderPrompt ?? DEFAULT_YIELD_CONFIG.reminderPrompt}`;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Register the submit_result tool handler in the subprocess tool registry.
|
|
180
|
-
*/
|
|
181
|
-
export function registerYieldTool(): void {
|
|
182
|
-
subprocessToolRegistry.register<YieldResult>(YIELD_TOOL_NAME, {
|
|
183
|
-
extractData(event: SubprocessToolEvent): YieldResult | undefined {
|
|
184
|
-
return extractYieldDataFromArgs(event.args, event.toolCallId);
|
|
185
|
-
},
|
|
186
|
-
shouldTerminate(): boolean {
|
|
187
|
-
return true;
|
|
188
|
-
},
|
|
189
|
-
});
|
|
190
|
-
}
|
|
1
|
+
import { subprocessToolRegistry, type SubprocessToolEvent } from "./subprocess-tool-registry.ts";
|
|
2
|
+
|
|
3
|
+
// G3: Full AJV-based schema validation for yield data.
|
|
4
|
+
// Falls back to lightweight validation if AJV is unavailable.
|
|
5
|
+
|
|
6
|
+
let _ajv: Awaited<ReturnType<typeof getAjvInternal>> | null | undefined;
|
|
7
|
+
|
|
8
|
+
async function getAjvInternal() {
|
|
9
|
+
// LAZY: AJV is an optional heavy validator — only load on first use.
|
|
10
|
+
const mod = await import("ajv");
|
|
11
|
+
const AjvCtor = ("default" in mod ? (mod as Record<string, unknown>).default : mod) as unknown as new (opts: Record<string, unknown>) => { compile: (schema: unknown) => { (data: unknown): boolean; errors?: unknown[] }; errorsText: (errors?: unknown[]) => string };
|
|
12
|
+
return new AjvCtor({ allErrors: true, strict: false, logger: false });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function getAjv() {
|
|
16
|
+
if (_ajv === undefined) {
|
|
17
|
+
try {
|
|
18
|
+
_ajv = await getAjvInternal();
|
|
19
|
+
} catch {
|
|
20
|
+
_ajv = null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return _ajv;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SchemaValidationResult {
|
|
27
|
+
valid: boolean;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validate yield data against a JSON Schema.
|
|
33
|
+
* Uses AJV when available (hoisted dep from pi-ai), falls back to
|
|
34
|
+
* lightweight type checking when not.
|
|
35
|
+
*/
|
|
36
|
+
export async function validateYieldData(data: unknown, schema: unknown): Promise<SchemaValidationResult> {
|
|
37
|
+
if (!schema) return { valid: true };
|
|
38
|
+
if (data === undefined || data === null) return { valid: false, error: "Yield data is null or undefined." };
|
|
39
|
+
|
|
40
|
+
// Try AJV first
|
|
41
|
+
const ajv = await getAjv();
|
|
42
|
+
if (ajv) {
|
|
43
|
+
try {
|
|
44
|
+
const validate = ajv.compile(schema as Record<string, unknown>);
|
|
45
|
+
const valid = validate(data);
|
|
46
|
+
if (!valid) {
|
|
47
|
+
return { valid: false, error: ajv.errorsText(validate.errors ?? undefined) };
|
|
48
|
+
}
|
|
49
|
+
return { valid: true };
|
|
50
|
+
} catch {
|
|
51
|
+
// AJV compile failed — fall through to lightweight
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Lightweight fallback
|
|
56
|
+
return lightweightValidate(data, schema);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function lightweightValidate(data: unknown, schema: unknown): SchemaValidationResult {
|
|
60
|
+
if (typeof schema === "boolean") return { valid: schema };
|
|
61
|
+
if (typeof schema !== "object" || Array.isArray(schema)) return { valid: true };
|
|
62
|
+
const schemaObj = schema as Record<string, unknown>;
|
|
63
|
+
|
|
64
|
+
// Check type constraint
|
|
65
|
+
if (typeof schemaObj.type === "string") {
|
|
66
|
+
const expected = schemaObj.type;
|
|
67
|
+
const actual = Array.isArray(data) ? "array" : typeof data;
|
|
68
|
+
if (expected === "object" && (typeof data !== "object" || Array.isArray(data))) return { valid: false, error: `Expected type '${expected}' but got '${actual}'.` };
|
|
69
|
+
if (expected === "array" && !Array.isArray(data)) return { valid: false, error: `Expected type 'array' but got '${actual}'.` };
|
|
70
|
+
if (expected === "string" && typeof data !== "string") return { valid: false, error: `Expected type 'string' but got '${actual}'.` };
|
|
71
|
+
if (expected === "number" && typeof data !== "number") return { valid: false, error: `Expected type 'number' but got '${actual}'.` };
|
|
72
|
+
if (expected === "boolean" && typeof data !== "boolean") return { valid: false, error: `Expected type 'boolean' but got '${actual}'.` };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check required fields
|
|
76
|
+
if (Array.isArray(schemaObj.required) && typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
77
|
+
const dataObj = data as Record<string, unknown>;
|
|
78
|
+
for (const field of schemaObj.required) {
|
|
79
|
+
if (typeof field === "string" && !(field in dataObj)) {
|
|
80
|
+
return { valid: false, error: `Missing required field '${field}'.` };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { valid: true };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface YieldResult {
|
|
89
|
+
summary: string;
|
|
90
|
+
artifacts?: Record<string, string>;
|
|
91
|
+
structuredData?: Record<string, unknown>;
|
|
92
|
+
toolCallId: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface YieldConfig {
|
|
96
|
+
enabled: boolean;
|
|
97
|
+
maxReminders: number;
|
|
98
|
+
reminderPrompt: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const DEFAULT_YIELD_CONFIG: YieldConfig = {
|
|
102
|
+
enabled: true,
|
|
103
|
+
maxReminders: 3,
|
|
104
|
+
reminderPrompt: "You must call the submit_result tool to return your results.",
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** Tool name used by workers to yield their result. */
|
|
108
|
+
export const YIELD_TOOL_NAME = "submit_result";
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Check if a value is a plain object record (non-null, non-array object).
|
|
112
|
+
*/
|
|
113
|
+
function isObjectRecord(value: unknown): value is Record<string, unknown> {
|
|
114
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if a value is a record with all string values.
|
|
119
|
+
*/
|
|
120
|
+
function isStringRecord(value: unknown): value is Record<string, string> {
|
|
121
|
+
if (!isObjectRecord(value)) return false;
|
|
122
|
+
return Object.values(value).every((v) => typeof v === "string");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Shared helper to extract yield data from tool call arguments.
|
|
127
|
+
* Used by both `extractYieldResult` and `registerYieldTool.extractData`
|
|
128
|
+
* to avoid duplicating parsing logic.
|
|
129
|
+
*/
|
|
130
|
+
export function extractYieldDataFromArgs(args: unknown, toolCallId: string): YieldResult | undefined {
|
|
131
|
+
if (!isObjectRecord(args)) return undefined;
|
|
132
|
+
const summary = typeof args.summary === "string" ? args.summary : "";
|
|
133
|
+
if (!summary) return undefined;
|
|
134
|
+
const result: YieldResult = { summary, toolCallId };
|
|
135
|
+
if (args.artifacts && isStringRecord(args.artifacts)) {
|
|
136
|
+
result.artifacts = args.artifacts;
|
|
137
|
+
}
|
|
138
|
+
if (args.structuredData && isObjectRecord(args.structuredData)) {
|
|
139
|
+
result.structuredData = args.structuredData;
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check if a JSON event represents a yield/submit_result tool call.
|
|
146
|
+
* Supports event types: tool_execution_start, toolCall, tool_call.
|
|
147
|
+
*/
|
|
148
|
+
export function isYieldEvent(event: Record<string, unknown>): boolean {
|
|
149
|
+
const type = event.type;
|
|
150
|
+
if (type !== "tool_execution_start" && type !== "toolCall" && type !== "tool_call") return false;
|
|
151
|
+
const toolName = event.toolName ?? event.name ?? event.tool;
|
|
152
|
+
return toolName === YIELD_TOOL_NAME;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Extract structured result from a yield event.
|
|
157
|
+
*/
|
|
158
|
+
export function extractYieldResult(event: Record<string, unknown>): YieldResult | undefined {
|
|
159
|
+
if (!isYieldEvent(event)) return undefined;
|
|
160
|
+
const toolCallId = typeof event.toolCallId === "string" ? event.toolCallId : "";
|
|
161
|
+
return extractYieldDataFromArgs(event.args, toolCallId);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Check if a worker output sequence contains a yield.
|
|
166
|
+
*/
|
|
167
|
+
export function hasYieldInOutput(events: Record<string, unknown>[]): boolean {
|
|
168
|
+
return events.some((event) => isYieldEvent(event));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Build a reminder prompt for workers that haven't yielded.
|
|
173
|
+
*/
|
|
174
|
+
export function buildYieldReminder(attempt: number, maxAttempts: number, reminderPrompt?: string): string {
|
|
175
|
+
return `[Yield Reminder ${attempt}/${maxAttempts}] ${reminderPrompt ?? DEFAULT_YIELD_CONFIG.reminderPrompt}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Register the submit_result tool handler in the subprocess tool registry.
|
|
180
|
+
*/
|
|
181
|
+
export function registerYieldTool(): void {
|
|
182
|
+
subprocessToolRegistry.register<YieldResult>(YIELD_TOOL_NAME, {
|
|
183
|
+
extractData(event: SubprocessToolEvent): YieldResult | undefined {
|
|
184
|
+
return extractYieldDataFromArgs(event.args, event.toolCallId);
|
|
185
|
+
},
|
|
186
|
+
shouldTerminate(): boolean {
|
|
187
|
+
return true;
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|