@piagent/platform 1.6.0 → 1.6.1
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 +49 -0
- package/README.md +17 -14
- package/README.vi.md +26 -1
- package/benchmarks/production-v1/spend-control.v1.json +27 -3
- package/benchmarks/production-v1/suite.json +5 -1
- package/catalog/capabilities.json +16 -16
- package/docs/authority-manifest.md +1 -1
- package/docs/auto-delegation-policy.md +38 -73
- package/docs/command-reference-vietnamese.md +23 -31
- package/docs/context-engine.md +5 -3
- package/docs/context-window-policy.md +37 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +26 -13
- package/docs/languages.json +4 -4
- package/docs/operator-manual-vietnamese.md +21 -41
- package/docs/publishing-for-teams.md +5 -0
- package/docs/quality-benchmark.md +179 -23
- package/docs/release-install-policy.md +6 -6
- package/docs/runtime-harness-standard.md +3 -3
- package/docs/security-threat-model.md +1 -1
- package/docs/subagent-orchestration-capabilities.md +19 -51
- package/docs/subagents-and-multiagent.md +21 -47
- package/docs/task-implementation-contract.md +3 -3
- package/docs/task-lifecycle-vietnamese.md +1 -1
- package/docs/team-onboarding.md +2 -2
- package/docs/vi/architecture.md +26 -13
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +89 -95
- package/docs-site/commands.html +1 -1
- package/docs-site/content/benchmark.html +88 -94
- package/docs-site/content/en/benchmark.html +134 -90
- package/docs-site/content/en/index.html +19 -19
- package/docs-site/content/en/runtime.html +4 -4
- package/docs-site/content/en/whats-new.html +12 -1
- package/docs-site/content/en/workflows.html +2 -2
- package/docs-site/content/index.html +19 -19
- package/docs-site/content/runtime.html +4 -4
- package/docs-site/content/whats-new.html +12 -1
- package/docs-site/content/workflows.html +2 -2
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +135 -91
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +20 -20
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +5 -5
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +13 -2
- package/docs-site/en/workflows.html +3 -3
- package/docs-site/index.html +20 -20
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +5 -5
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +13 -2
- package/docs-site/workflows.html +3 -3
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
- package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
- package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
- package/evals/fixtures/helper-request.invalid.json +1 -1
- package/evals/fixtures/helper-request.valid.json +1 -1
- package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
- package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
- package/evals/fs4-readiness-matrix.v1.json +2 -2
- package/evals/fs5-causal-arm.v1.json +2 -2
- package/evals/fs5-pilot-protocol.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v2.json +8 -8
- package/evals/fs5-pilot-protocol.v3.json +8 -8
- package/evals/fs5-pilot-protocol.v4.json +6 -6
- package/evals/fs5-pilot-protocol.v5.json +7 -6
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/evals/runtime-conformance-v1/README.md +43 -0
- package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
- package/evals/runtime-conformance-v1/lane.json +57 -0
- package/evals/runtime-conformance-v1/paid-canary.json +31 -0
- package/evals/runtime-conformance-v1/runner.mjs +456 -0
- package/package.json +1 -1
- package/packages/piagent-core/README.md +13 -4
- package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
- package/packages/piagent-core/benchmark/benchmark-core.js +4 -3
- package/packages/piagent-core/benchmark/benchmark-forensics.js +4 -2
- package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +74 -5
- package/packages/piagent-core/benchmark/benchmark-report.js +15 -1
- package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +201 -4
- package/packages/piagent-core/benchmark/benchmark-suite.js +22 -1
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +4 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +15 -4
- package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
- package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
- package/packages/piagent-core/extensions/acceptance-receipt.js +89 -48
- package/packages/piagent-core/extensions/context-engine.js +1 -11
- package/packages/piagent-core/extensions/context-index-policy.js +4 -0
- package/packages/piagent-core/extensions/context-query-paths.js +57 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +16 -3
- package/packages/piagent-core/extensions/criterion-graph.js +4 -1
- package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
- package/packages/piagent-core/extensions/guard-types.ts +4 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +194 -226
- package/packages/piagent-core/extensions/task-lifecycle.js +32 -0
- package/packages/piagent-core/extensions/task-state.js +69 -8
- package/packages/piagent-core/extensions/verification-intelligence.js +49 -12
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +52 -16
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +42 -2
- package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
- package/packages/piagent-core/prompts/be-to-fe.md +2 -8
- package/packages/piagent-core/prompts/plan.md +1 -1
- package/packages/piagent-core/prompts/platform-improve.md +2 -6
- package/packages/piagent-core/prompts/review.md +2 -7
- package/packages/piagent-core/prompts/scout.md +1 -1
- package/packages/piagent-core/prompts/task.md +4 -3
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +7 -6
- package/packages/piagent-core/runtime/hooks/input-hook.ts +6 -0
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +100 -1
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +58 -72
- package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
- package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
- package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
- package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +20 -3
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +47 -11
- package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
- package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
- package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
- package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
- package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
- package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +3 -0
- package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +20 -10
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +3 -3
- package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
- package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
- package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
- package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
- package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
- package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +7 -13
- package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
- package/packages/piagent-core/runtime/registration/context-commands.ts +16 -2
- package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
- package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
- package/packages/piagent-core/runtime/registration/policy-tools.ts +4 -4
- package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +12 -9
- package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +19 -13
- package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +57 -57
- package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
- package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
- package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
- package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
- package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
- package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
- package/packages/piagent-core/runtime/session/runtime-state.ts +11 -7
- package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
- package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
- package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
- package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
- package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
- package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
- package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
- package/packages/piagent-core/runtime/workflows/task-intake.ts +167 -39
- package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
- package/packages/piagent-core/subagents/piagent-planner.md +6 -4
- package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
- package/packages/piagent-core/subagents/piagent-scout.md +4 -2
- package/packages/piagent-core/subagents/piagent-worker.md +6 -4
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-CxeZqTte.js → MarkdownMessage-B4XehTCU.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-DynIMe8I.js → index-CgOEyRPG.js} +4 -4
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
- package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
- package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +37 -21
- package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
- package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +9 -2
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +8 -7
- package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
- package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
- package/packages/piagent-webui/server/transcript-projection.ts +91 -5
- package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
- package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
- package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
- package/schemas/project-profile.schema.json +1 -1
- package/schemas/task-contract.schema.json +15 -0
- package/scripts/benchmark-runner-core.mjs +14 -6
- package/scripts/benchmark-runner-finalization.mjs +4 -0
- package/scripts/configure-subagents.sh +42 -34
- package/scripts/helper-evaluation.mjs +1 -1
- package/scripts/team-doctor.sh +1 -1
- package/scripts/verify-local.sh +5 -0
|
@@ -11,11 +11,11 @@ export function registerKnowledgeTools(pi: ExtensionAPI, deps: Record<string, an
|
|
|
11
11
|
registerPiagentTool(pi, {
|
|
12
12
|
name: "piagent_orchestration_policy",
|
|
13
13
|
label: "Piagent Orchestration Policy",
|
|
14
|
-
description: "Return
|
|
15
|
-
promptSnippet: "Use this before planning medium/large tasks so
|
|
14
|
+
description: "Return parent-direct helper limits, review lenses, model-role, and Field Guide policy for the current project.",
|
|
15
|
+
promptSnippet: "Use this before planning medium/large tasks so the parent stays direct and helper use remains a measured token optimization.",
|
|
16
16
|
promptGuidelines: [
|
|
17
|
-
"Default to the parent
|
|
18
|
-
"Use
|
|
17
|
+
"Default to the parent model working directly; review quality alone does not justify another model run.",
|
|
18
|
+
"Use at most one fresh read-only helper only with two independent lanes and at least 30% projected total token savings; never delegate implementation or retry a deterministic failure.",
|
|
19
19
|
"Treat Field Guide memory as advisory and verify it against current repository files."
|
|
20
20
|
],
|
|
21
21
|
parameters: Type.Object({
|
|
@@ -45,7 +45,9 @@ export function registerKnowledgeTools(pi: ExtensionAPI, deps: Record<string, an
|
|
|
45
45
|
path: fieldGuidePath,
|
|
46
46
|
exists: fieldGuideExists
|
|
47
47
|
},
|
|
48
|
-
|
|
48
|
+
helperBudget: "1-readonly-total/0-retries/0-writers",
|
|
49
|
+
minimumProjectedNetSaving: 0.3,
|
|
50
|
+
stance: "parent-direct; one fresh read-only helper only when runtime-measured net token savings justify it"
|
|
49
51
|
};
|
|
50
52
|
const text = params.detail === "full"
|
|
51
53
|
? JSON.stringify(payload, null, 2)
|
|
@@ -53,6 +55,8 @@ export function registerKnowledgeTools(pi: ExtensionAPI, deps: Record<string, an
|
|
|
53
55
|
`mode: ${payload.defaultMode}`,
|
|
54
56
|
`maxConcurrentSubagents: ${payload.maxConcurrentSubagents}`,
|
|
55
57
|
`helpersMode: ${payload.helpersMode}`,
|
|
58
|
+
`helperBudget: ${payload.helperBudget}`,
|
|
59
|
+
`minimumProjectedNetSaving: ${Math.round(payload.minimumProjectedNetSaving * 100)}%`,
|
|
56
60
|
`reviewLenses: ${payload.defaultReviewLenses.join(", ")}`,
|
|
57
61
|
`fieldGuide: ${payload.fieldGuide.enabled ? `${payload.fieldGuide.path} (${payload.fieldGuide.exists ? "exists" : "missing"})` : "off"}`,
|
|
58
62
|
`fieldGuidePolicy: ${payload.fieldGuide.writePolicy}, maxLines=${payload.fieldGuide.maxLines}`,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { OPERATOR_REQUEST_MAX_CHARS } from "../../extensions/task-state.js";
|
|
2
|
+
|
|
3
|
+
type SessionContext = {
|
|
4
|
+
sessionManager: {
|
|
5
|
+
getBranch?: () => unknown;
|
|
6
|
+
getEntries?: () => unknown;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function userMessageText(message: unknown): string {
|
|
11
|
+
if (!message || typeof message !== "object" || (message as Record<string, unknown>).role !== "user") return "";
|
|
12
|
+
const content = (message as Record<string, unknown>).content;
|
|
13
|
+
if (typeof content === "string") return content;
|
|
14
|
+
if (!Array.isArray(content)) return "";
|
|
15
|
+
// The host stores the exact operator prose in the first text block. Later
|
|
16
|
+
// text blocks may contain extracted attachment bodies and must not be copied
|
|
17
|
+
// into private task truth as if the operator typed them.
|
|
18
|
+
const first = content.find((block) => block && typeof block === "object"
|
|
19
|
+
&& (block as Record<string, unknown>).type === "text"
|
|
20
|
+
&& typeof (block as Record<string, unknown>).text === "string") as Record<string, unknown> | undefined;
|
|
21
|
+
return typeof first?.text === "string" ? first.text : "";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function latestOperatorRequest(ctx: SessionContext): string | undefined {
|
|
25
|
+
let entries: unknown[] = [];
|
|
26
|
+
try {
|
|
27
|
+
const branch = ctx.sessionManager.getBranch?.();
|
|
28
|
+
entries = Array.isArray(branch) ? branch : [];
|
|
29
|
+
if (entries.length === 0) {
|
|
30
|
+
const all = ctx.sessionManager.getEntries?.();
|
|
31
|
+
entries = Array.isArray(all) ? all : [];
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
37
|
+
const entry = entries[index] as Record<string, any> | undefined;
|
|
38
|
+
if (entry?.type !== "message") continue;
|
|
39
|
+
const text = userMessageText(entry.message);
|
|
40
|
+
if (text.trim()) return text;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function boundedOperatorRequest(value: unknown, redactText: (value: string) => string): string | undefined {
|
|
46
|
+
if (typeof value !== "string" || !value.trim()) return undefined;
|
|
47
|
+
const redacted = redactText(value);
|
|
48
|
+
return Array.from(redacted).length <= OPERATOR_REQUEST_MAX_CHARS ? redacted : undefined;
|
|
49
|
+
}
|
|
@@ -30,7 +30,7 @@ export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>)
|
|
|
30
30
|
"When recovery is necessary, load only the smallest group that resolves the reported missing evidence."
|
|
31
31
|
],
|
|
32
32
|
parameters: Type.Object({
|
|
33
|
-
groups: Type.Array(StringEnum(["intake", "governance", "task", "recovery", "policy", "retrieval", "knowledge", "onboarding", "usage"] as const), { minItems: 1 })
|
|
33
|
+
groups: Type.Array(StringEnum(["intake", "governance", "task", "recovery", "policy", "retrieval", "source", "knowledge", "onboarding", "usage"] as const), { minItems: 1 })
|
|
34
34
|
}),
|
|
35
35
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
36
36
|
const groups = [...new Set(params.groups)] as PiagentToolGroup[];
|
|
@@ -429,8 +429,8 @@ export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>)
|
|
|
429
429
|
registerPiagentTool(pi, {
|
|
430
430
|
name: "piagent_task_gate_check",
|
|
431
431
|
label: "Piagent Task Gate Check",
|
|
432
|
-
description: "Check whether a governed task has enough context,
|
|
433
|
-
promptSnippet: "Use
|
|
432
|
+
description: "Check whether a governed task has enough context, current-tree exact verifier evidence, and trace before claiming done or rerunning verification.",
|
|
433
|
+
promptSnippet: "Use before final and before repeating an exact verifier; run only commands listed as missing for the current tree.",
|
|
434
434
|
parameters: Type.Object({
|
|
435
435
|
taskId: Type.String({ minLength: 1 }),
|
|
436
436
|
changedFiles: Type.Optional(Type.Array(Type.String()))
|
|
@@ -451,7 +451,7 @@ export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>)
|
|
|
451
451
|
...verifierCommandInstructions(result.missingVerifyCommands),
|
|
452
452
|
`warnings: ${result.warnings.join("; ") || "none"}`
|
|
453
453
|
].join("\n");
|
|
454
|
-
return { content: [{ type: "text", text }], details: { ...result, task: projected } };
|
|
454
|
+
return { content: [{ type: "text", text }], details: { ...result, task: projected ? { taskId: projected.taskId, taskRunId: projected.taskRunId } : undefined } };
|
|
455
455
|
}
|
|
456
456
|
});
|
|
457
457
|
|
|
@@ -334,7 +334,7 @@ export function registerSessionCommands(pi: ExtensionAPI, deps: Record<string, a
|
|
|
334
334
|
const approvals = feature ? [feature.externalAction ? "external-action" : "", feature.destructiveAction ? "destructive-action" : "", feature.permissionExpansion ? "permission-expansion" : ""].filter(Boolean) : [];
|
|
335
335
|
const modelRoute = feature ? await evaluateModelRoute?.(ctx, feature, snapshot) : undefined;
|
|
336
336
|
const retrievalRoute = feature ? await evaluateRetrievalRoute?.(ctx, feature) : undefined;
|
|
337
|
-
return { runtime: snapshot, modelRoute: modelRoute?.status === "ok" ? modelRoute.decision : null, retrievalRoute: retrievalRoute ?? null, scope: task?.scope ?? [], protectedPaths: effectiveProtectedPaths(policy, profile).readProtectedPaths, activeToolGroups: pi.getActiveTools(), helperMode: helpersMode(), helperBudget: "
|
|
337
|
+
return { runtime: snapshot, modelRoute: modelRoute?.status === "ok" ? modelRoute.decision : null, retrievalRoute: retrievalRoute ?? null, scope: task?.scope ?? [], protectedPaths: effectiveProtectedPaths(policy, profile).readProtectedPaths, activeToolGroups: pi.getActiveTools(), helperMode: helpersMode(), helperBudget: "1-readonly-total/0-retries/0-writers", executionBackend: "host", executionBoundary: "host execution is not a sandbox", approvals, blockers: feature?.protectedTarget ? ["protected-target"] : [], controlMode: evaluation.status === "ok" && evaluation.decision.mode === "shadow" ? "shadow" : resolveRuntimePolicy(profile).finalGate === "enforce" ? "enforce" : "assist" };
|
|
338
338
|
}
|
|
339
339
|
});
|
|
340
340
|
|
|
@@ -9,6 +9,9 @@ import { readTrajectoryStore } from "../trajectory/trajectory-store.ts";
|
|
|
9
9
|
|
|
10
10
|
type TaskContract = any;
|
|
11
11
|
|
|
12
|
+
function taskErrorDetails(task: TaskContract, reasonCode: string): Record<string, unknown> {
|
|
13
|
+
return { reasonCode, taskId: task.taskId, taskRunId: task.taskRunId, outcome: task.trace?.outcome, attempt: task.attempt };
|
|
14
|
+
}
|
|
12
15
|
|
|
13
16
|
export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<string, any>): void {
|
|
14
17
|
const {
|
|
@@ -41,7 +44,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
41
44
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], isError: true };
|
|
42
45
|
}
|
|
43
46
|
if (task.trace.outcome !== "pending") {
|
|
44
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; context evidence was not changed.` }], details: task, isError: true };
|
|
47
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; context evidence was not changed.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
45
48
|
}
|
|
46
49
|
const activeIdentity = runtimeState.taskIdentity(ctx);
|
|
47
50
|
if (!activeIdentity || activeIdentity.taskId !== task.taskId || activeIdentity.taskRunId !== task.taskRunId) {
|
|
@@ -127,7 +130,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
127
130
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], isError: true };
|
|
128
131
|
}
|
|
129
132
|
if (task.trace.outcome !== "pending") {
|
|
130
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; verify evidence was not changed.` }], details: task, isError: true };
|
|
133
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; verify evidence was not changed.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
const observedEntries = [
|
|
@@ -239,7 +242,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
239
242
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], isError: true };
|
|
240
243
|
}
|
|
241
244
|
if (task.trace.outcome !== "pending") {
|
|
242
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; memory evidence was not changed.` }], details: task, isError: true };
|
|
245
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; memory evidence was not changed.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
243
246
|
}
|
|
244
247
|
|
|
245
248
|
const safeFiles = params.files.map((file) => ({
|
|
@@ -257,7 +260,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
257
260
|
|
|
258
261
|
return {
|
|
259
262
|
content: [{ type: "text", text: `Memory citations recorded for ${task.taskId}: ${params.files.length} file(s)` }],
|
|
260
|
-
details: task
|
|
263
|
+
details: compactTaskDetails(task)
|
|
261
264
|
};
|
|
262
265
|
}
|
|
263
266
|
});
|
|
@@ -284,13 +287,13 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
284
287
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], isError: true };
|
|
285
288
|
}
|
|
286
289
|
if (task.trace.outcome !== "pending") {
|
|
287
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; its final trace was not replaced.` }], details: task, isError: true };
|
|
290
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; its final trace was not replaced.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
288
291
|
}
|
|
289
292
|
if (params.outcome !== "completed" && !params.friction?.trim() && !task.failureReason?.trim()) {
|
|
290
|
-
return { content: [{ type: "text", text: `Trace ${params.outcome} requires a concrete friction/reason so the next attempt does not repeat the same work.` }], details: task, isError: true };
|
|
293
|
+
return { content: [{ type: "text", text: `Trace ${params.outcome} requires a concrete friction/reason so the next attempt does not repeat the same work.` }], details: taskErrorDetails(task, "trace-friction-required"), isError: true };
|
|
291
294
|
}
|
|
292
295
|
if (params.outcome === "failed" && !params.failedAt && !task.failedAt) {
|
|
293
|
-
return { content: [{ type: "text", text: "A failed trace requires failedAt to identify the lifecycle phase." }], details: task, isError: true };
|
|
296
|
+
return { content: [{ type: "text", text: "A failed trace requires failedAt to identify the lifecycle phase." }], details: taskErrorDetails(task, "trace-failure-phase-required"), isError: true };
|
|
294
297
|
}
|
|
295
298
|
|
|
296
299
|
const rawChangedFiles = params.changedFiles ?? task.changedFiles;
|
|
@@ -363,7 +366,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
363
366
|
...verifierCommandInstructions(gate.missingVerifyCommands)
|
|
364
367
|
].join("\n")
|
|
365
368
|
}],
|
|
366
|
-
details: { gate, task: nextTask },
|
|
369
|
+
details: { gate, task: taskErrorDetails(nextTask, "completion-gate-blocked") },
|
|
367
370
|
isError: true
|
|
368
371
|
};
|
|
369
372
|
}
|
|
@@ -426,7 +429,7 @@ export function registerTaskCompletionTools(pi: ExtensionAPI, deps: Record<strin
|
|
|
426
429
|
|
|
427
430
|
return {
|
|
428
431
|
content: [{ type: "text", text: `Trace recorded for ${nextTask.taskId}: ${params.outcome}${gate.decision === "fail" ? ` (gate warning: missing ${gate.missing.join(", ")})` : ""}` }],
|
|
429
|
-
details: { task: written, gate, completionReceipt: buildCompletionReceiptView(written, { cwd: ctx.cwd, gate }) }
|
|
432
|
+
details: { task: compactTaskDetails(written), gate, completionReceipt: buildCompletionReceiptView(written, { cwd: ctx.cwd, gate }) }
|
|
430
433
|
};
|
|
431
434
|
}
|
|
432
435
|
});
|
|
@@ -2,6 +2,9 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
|
|
3
3
|
type WorkPlanStep = any;
|
|
4
4
|
|
|
5
|
+
function taskErrorDetails(task: any, reasonCode: string): Record<string, unknown> {
|
|
6
|
+
return { reasonCode, taskId: task.taskId, taskRunId: task.taskRunId, outcome: task.trace?.outcome, attempt: task.attempt };
|
|
7
|
+
}
|
|
5
8
|
|
|
6
9
|
export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string, any>): void {
|
|
7
10
|
const {
|
|
@@ -9,7 +12,7 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
9
12
|
compactTaskDetails, effectiveProtectedPaths, extensionDir, extractDocument, loadProfileFromContext,
|
|
10
13
|
matchesAnyPath, matchesProtectedPath, nowIso, path, permissionOverrideFromContext,
|
|
11
14
|
policy, readTask, recordTaskProgressCheckpoints, redactText, registerPiagentTool,
|
|
12
|
-
resolveDocumentPath, resolveDocumentRoots, resolvePermissionProfile, safeTaskId, verifyProjectCapabilityState,
|
|
15
|
+
resolveDocumentPath, resolveDocumentRoots, resolvePermissionProfile, runtimeState, safeTaskId, verifyProjectCapabilityState,
|
|
13
16
|
writeTask
|
|
14
17
|
} = deps;
|
|
15
18
|
registerPiagentTool(pi, {
|
|
@@ -37,24 +40,26 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
37
40
|
return { content: [{ type: "text", text: `Task not found in this session: ${params.taskId}` }], isError: true };
|
|
38
41
|
}
|
|
39
42
|
if (task.trace.outcome !== "pending") {
|
|
40
|
-
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; start a fresh session for another attempt.` }], details: task, isError: true };
|
|
43
|
+
return { content: [{ type: "text", text: `Task ${task.taskId} is immutable after ${task.trace.outcome}; start a fresh session for another attempt.` }], details: taskErrorDetails(task, "task-immutable"), isError: true };
|
|
41
44
|
}
|
|
42
45
|
const stepId = safeTaskId(params.stepId).slice(0, 40);
|
|
43
46
|
const step = task.workPlan.find((item) => item.id === stepId);
|
|
44
47
|
if (!step) {
|
|
45
|
-
const
|
|
48
|
+
const validSteps = task.workPlan.slice(0, 20).map((item) => ({ id: item.id, status: item.status }));
|
|
49
|
+
const valid = validSteps.map((item) => `${item.id} (${item.status})`).join(", ");
|
|
46
50
|
const actionable = task.workPlan
|
|
47
51
|
.filter((item) => item.status === "in-progress" || (
|
|
48
52
|
item.status === "pending"
|
|
49
53
|
&& (item.dependsOn ?? []).every((dependency) => task.workPlan.some((candidate) => candidate.id === dependency && ["done", "skipped"].includes(candidate.status)))
|
|
50
54
|
))
|
|
51
|
-
.map((item) => item.id)
|
|
55
|
+
.map((item) => item.id)
|
|
56
|
+
.slice(0, 20);
|
|
52
57
|
return {
|
|
53
58
|
content: [{
|
|
54
59
|
type: "text",
|
|
55
60
|
text: `Work-plan step not found: ${stepId}. Valid step IDs: ${valid || "none"}. Currently actionable: ${actionable.join(", ") || "none"}.`
|
|
56
61
|
}],
|
|
57
|
-
details: task
|
|
62
|
+
details: { ...taskErrorDetails(task, "work-plan-step-not-found"), stepId, validSteps, actionable },
|
|
58
63
|
isError: true
|
|
59
64
|
};
|
|
60
65
|
}
|
|
@@ -66,7 +71,7 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
66
71
|
if ((params.status === "in-progress" || params.status === "done") && unresolved.length > 0) {
|
|
67
72
|
return {
|
|
68
73
|
content: [{ type: "text", text: `Step ${stepId} is blocked by unfinished dependencies: ${unresolved.join(", ")}` }],
|
|
69
|
-
details: { step, unresolved },
|
|
74
|
+
details: { ...taskErrorDetails(task, "work-plan-step-blocked"), stepId, unresolved: unresolved.slice(0, 20) },
|
|
70
75
|
isError: true
|
|
71
76
|
};
|
|
72
77
|
}
|
|
@@ -75,13 +80,13 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
75
80
|
return { content: [{ type: "text", text: `A concrete note is required when step ${stepId} fails.` }], isError: true };
|
|
76
81
|
}
|
|
77
82
|
if ((step.status === "done" || step.status === "skipped") && params.status !== step.status) {
|
|
78
|
-
return { content: [{ type: "text", text: `Work-plan step ${stepId} is already ${step.status} and cannot be reopened in the same attempt.` }], details: step, isError: true };
|
|
83
|
+
return { content: [{ type: "text", text: `Work-plan step ${stepId} is already ${step.status} and cannot be reopened in the same attempt.` }], details: { ...taskErrorDetails(task, "work-plan-step-immutable"), stepId, status: step.status }, isError: true };
|
|
79
84
|
}
|
|
80
85
|
if (step.status === params.status) {
|
|
81
|
-
return { content: [{ type: "text", text: `Work-plan step ${stepId} is already ${params.status}; no state change was recorded.` }], details: step, isError: true };
|
|
86
|
+
return { content: [{ type: "text", text: `Work-plan step ${stepId} is already ${params.status}; no state change was recorded.` }], details: { ...taskErrorDetails(task, "work-plan-step-unchanged"), stepId, status: step.status }, isError: true };
|
|
82
87
|
}
|
|
83
88
|
if (step.status === "failed" && params.status === "in-progress" && !note) {
|
|
84
|
-
return { content: [{ type: "text", text: `A concrete note is required to reopen failed step ${stepId} within this attempt.` }], details: step, isError: true };
|
|
89
|
+
return { content: [{ type: "text", text: `A concrete note is required to reopen failed step ${stepId} within this attempt.` }], details: { ...taskErrorDetails(task, "work-plan-step-reopen-note-required"), stepId, status: step.status }, isError: true };
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
const recordedAt = nowIso();
|
|
@@ -254,7 +259,7 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
254
259
|
registerPiagentTool(pi, {
|
|
255
260
|
name: "piagent_source_checkout",
|
|
256
261
|
label: "Piagent Source Checkout",
|
|
257
|
-
description: "
|
|
262
|
+
description: "Prepare an external Git repository for bounded, read-only local inspection.",
|
|
258
263
|
promptSnippet: "Use this before reading a user-provided external source repository.",
|
|
259
264
|
promptGuidelines: [
|
|
260
265
|
"Use for GitHub/GitLab/Bitbucket source repositories supplied by the user.",
|
|
@@ -264,15 +269,19 @@ export function registerTaskEvidenceTools(pi: ExtensionAPI, deps: Record<string,
|
|
|
264
269
|
repoRef: Type.String({ minLength: 3, description: "owner/repo, host/owner/repo, https URL, or git@host:owner/repo.git" }),
|
|
265
270
|
forceUpdate: Type.Optional(Type.Boolean({ description: "Fetch immediately even if the cache was refreshed recently." }))
|
|
266
271
|
}),
|
|
267
|
-
async execute(_toolCallId, params) {
|
|
272
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
268
273
|
try {
|
|
269
274
|
const repo = checkoutReferenceRepo(params.repoRef, params.forceUpdate === true);
|
|
275
|
+
const grantedPath = runtimeState.grantSourceCheckoutReadRoot(ctx, repo.checkoutPath);
|
|
276
|
+
repo.checkoutPath = grantedPath;
|
|
270
277
|
const text = [
|
|
271
278
|
"Source cache ready:",
|
|
272
279
|
`path: ${repo.checkoutPath}`,
|
|
273
280
|
`url: ${repo.cloneUrl}`,
|
|
274
281
|
`commit: ${repo.commit ?? "unknown"}`,
|
|
275
|
-
`fetched: ${repo.fetched ? "yes" : "no"}
|
|
282
|
+
`fetched: ${repo.fetched ? "yes" : "no"}`,
|
|
283
|
+
"access: read-only for this session via read, grep, find, or ls",
|
|
284
|
+
"Do not run a checkout script or use shell mutation against this shared cache."
|
|
276
285
|
].join("\n");
|
|
277
286
|
return {
|
|
278
287
|
content: [{ type: "text", text }],
|
|
@@ -102,12 +102,18 @@ export function criticalProofSection(
|
|
|
102
102
|
acceptanceTests: FocusedTestCandidate[],
|
|
103
103
|
criterionProofs: CriterionProofGuidance[]
|
|
104
104
|
): string[] {
|
|
105
|
-
if (proofGuidance.length === 0) return [];
|
|
105
|
+
if (proofGuidance.length === 0 && criterionProofs.length === 0) return [];
|
|
106
106
|
const candidateMap = new Map<string, string[]>();
|
|
107
107
|
for (const candidate of acceptanceTests) {
|
|
108
108
|
candidateMap.set(candidate.criterionId, [...new Set([...(candidateMap.get(candidate.criterionId) ?? []), candidate.path])]);
|
|
109
109
|
}
|
|
110
110
|
let mappedCandidate = false;
|
|
111
|
+
const criterionTag = (criterionId: string): string => {
|
|
112
|
+
const candidates = candidateMap.get(criterionId) ?? [];
|
|
113
|
+
if (candidates.length === 0) return `[${criterionId}:fallback]`;
|
|
114
|
+
mappedCandidate = true;
|
|
115
|
+
return `[${criterionId}:candidate=${candidates.join("|")}]`;
|
|
116
|
+
};
|
|
111
117
|
const proofLines = proofGuidance.flatMap((proof) => {
|
|
112
118
|
const criterionIds = [...new Set(criterionProofs
|
|
113
119
|
.filter((entry) => entry.guidance.includes(proof))
|
|
@@ -115,21 +121,19 @@ export function criticalProofSection(
|
|
|
115
121
|
if (criterionIds.length === 0) {
|
|
116
122
|
return [`- [fallback] ${proof}`];
|
|
117
123
|
}
|
|
118
|
-
const tags = criterionIds.map(
|
|
119
|
-
const candidates = candidateMap.get(criterionId) ?? [];
|
|
120
|
-
if (candidates.length === 0) {
|
|
121
|
-
return `[${criterionId}:fallback]`;
|
|
122
|
-
}
|
|
123
|
-
mappedCandidate = true;
|
|
124
|
-
return `[${criterionId}:candidate=${candidates.join("|")}]`;
|
|
125
|
-
});
|
|
124
|
+
const tags = criterionIds.map(criterionTag);
|
|
126
125
|
return [`- ${tags.join("")} ${proof}`];
|
|
127
126
|
});
|
|
127
|
+
const genericTags = criterionProofs.filter((entry) => entry.guidance.length === 0).map((entry) => criterionTag(entry.criterionId));
|
|
128
|
+
const genericLines = genericTags.length > 0
|
|
129
|
+
? [`- ${genericTags.join("")} Prove every tagged observable clause with linked live assertions; the generic verifier alone is insufficient.`]
|
|
130
|
+
: [];
|
|
128
131
|
return [[
|
|
129
132
|
"Critical behavioral proof:",
|
|
130
|
-
...(!mappedCandidate ? ["No concrete criterion-linked focused test was selected."] : []),
|
|
133
|
+
...(!mappedCandidate && genericTags.length === 0 ? ["No concrete criterion-linked focused test was selected."] : []),
|
|
131
134
|
...proofLines,
|
|
132
|
-
|
|
135
|
+
...genericLines,
|
|
136
|
+
"Candidate tags are locations, not proof; add/update durable scoped tests with live assertions and changed-test evidence. Each fallback tag requires adding/updating a durable scoped focused test with live assertions. If test scope is unavailable, report missing durable proof and do not claim completion. Run proof after the criterion's final intended mutation and before exact verifiers; rerun after later target mutation or a runtime-authorized same-tree infrastructure retry. Transient/print-only probes and prose are insufficient. Claims must match diff/tests; never claim unproved behavior."
|
|
133
137
|
].join("\n")];
|
|
134
138
|
}
|
|
135
139
|
|
|
@@ -150,7 +154,8 @@ export function taskCriticalProofSection(
|
|
|
150
154
|
repositoryFiles: string[],
|
|
151
155
|
acceptanceProofGuidance: (taskOrText: unknown) => string[],
|
|
152
156
|
isAcceptanceTestPath: (file: string) => boolean,
|
|
153
|
-
acceptanceLanguageAdapterForPath: (file: string) => { disposition?: string }
|
|
157
|
+
acceptanceLanguageAdapterForPath: (file: string) => { disposition?: string },
|
|
158
|
+
requireGenericFallback = true
|
|
154
159
|
): string[] {
|
|
155
160
|
const proofGuidance = task.changeMode === "source-change" && task.mutationPolicy !== "forbidden"
|
|
156
161
|
? acceptanceProofGuidance(task)
|
|
@@ -158,6 +163,7 @@ export function taskCriticalProofSection(
|
|
|
158
163
|
const acceptanceTests = concretePlannedAcceptanceTests(
|
|
159
164
|
plannedContext, repositoryFiles, task.criterionGraph, isAcceptanceTestPath, acceptanceLanguageAdapterForPath
|
|
160
165
|
);
|
|
161
|
-
const criterionProofs = behavioralCriterionProofGuidance(task.criterionGraph, acceptanceProofGuidance)
|
|
166
|
+
const criterionProofs = behavioralCriterionProofGuidance(task.criterionGraph, acceptanceProofGuidance)
|
|
167
|
+
.filter((entry) => requireGenericFallback || entry.guidance.length > 0);
|
|
162
168
|
return criticalProofSection(proofGuidance, acceptanceTests, criterionProofs);
|
|
163
169
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
type RepositoryManifestCompatibilityReason = "missing-details-provider" | "nonfunction-details-provider" | "invalid-details-result";
|
|
2
|
+
|
|
3
|
+
type TaskStartRepositoryManifest = {
|
|
4
|
+
files: string[];
|
|
5
|
+
complete: boolean;
|
|
6
|
+
candidateCount: number;
|
|
7
|
+
provider: "details" | "legacy-array-fallback";
|
|
8
|
+
compatibilityReason?: RepositoryManifestCompatibilityReason;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function normalizedManifestFiles(value: unknown): string[] {
|
|
12
|
+
if (!Array.isArray(value)) return [];
|
|
13
|
+
return [...new Set(value.filter((item): item is string => typeof item === "string" && item.trim().length > 0))];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Keep rolling upgrades safe when a long-lived host has the legacy extension
|
|
18
|
+
* entrypoint cached but imports a newer task-start registration module. The
|
|
19
|
+
* legacy array provider cannot prove whether its result was truncated, so the
|
|
20
|
+
* compatibility path deliberately reports `complete: false` and records the
|
|
21
|
+
* provider/reason in the durable task trace.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveTaskStartRepositoryManifestProvider(deps: Record<string, any>): {
|
|
24
|
+
read(cwd: string, maxFiles?: number): TaskStartRepositoryManifest;
|
|
25
|
+
} {
|
|
26
|
+
const legacyProvider = deps.repositoryFileManifest;
|
|
27
|
+
if (typeof legacyProvider !== "function") {
|
|
28
|
+
throw new TypeError("Piagent task-start registration requires repositoryFileManifest to be a function.");
|
|
29
|
+
}
|
|
30
|
+
const detailsProvider = deps.repositoryFileManifestDetails;
|
|
31
|
+
const compatibilityReason: RepositoryManifestCompatibilityReason | undefined = detailsProvider === undefined
|
|
32
|
+
? "missing-details-provider"
|
|
33
|
+
: typeof detailsProvider !== "function"
|
|
34
|
+
? "nonfunction-details-provider"
|
|
35
|
+
: undefined;
|
|
36
|
+
const legacyManifest = (cwd: string, maxFiles?: number,
|
|
37
|
+
reason: RepositoryManifestCompatibilityReason = compatibilityReason ?? "invalid-details-result"): TaskStartRepositoryManifest => {
|
|
38
|
+
const files = normalizedManifestFiles(legacyProvider(cwd, maxFiles));
|
|
39
|
+
return { files, complete: false, candidateCount: files.length, provider: "legacy-array-fallback", compatibilityReason: reason };
|
|
40
|
+
};
|
|
41
|
+
if (compatibilityReason) return { read: legacyManifest };
|
|
42
|
+
return {
|
|
43
|
+
read(cwd: string, maxFiles?: number): TaskStartRepositoryManifest {
|
|
44
|
+
const details = detailsProvider(cwd, maxFiles);
|
|
45
|
+
if (!details || typeof details !== "object" || !Array.isArray(details.files)) {
|
|
46
|
+
return legacyManifest(cwd, maxFiles, "invalid-details-result");
|
|
47
|
+
}
|
|
48
|
+
const files = normalizedManifestFiles(details.files);
|
|
49
|
+
return { files, complete: details.complete === true,
|
|
50
|
+
candidateCount: Number.isInteger(details.candidateCount) ? Math.max(files.length, details.candidateCount) : files.length,
|
|
51
|
+
provider: "details" };
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|