@piagent/platform 1.5.4 → 1.6.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 +136 -0
- package/README.md +9 -8
- package/README.vi.md +9 -5
- package/adapters/web-frontend/profile.json +2 -4
- package/architecture/layers.json +18 -3
- package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
- package/benchmarks/deep-logic-v1/grade.mjs +272 -10
- package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
- package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
- package/benchmarks/deep-logic-v1/project/package.json +1 -1
- package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
- package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
- package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
- package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
- package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
- package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
- package/benchmarks/deep-logic-v1/suite.json +51 -3
- package/benchmarks/deep-logic-v1/variant.mjs +22 -1
- package/benchmarks/production-v1/spend-control.v1.json +68 -0
- package/benchmarks/production-v1/suite.json +42 -3
- package/catalog/capabilities.json +6 -6
- package/docs/command-reference-vietnamese.md +6 -6
- package/docs/context-engine.md +7 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +1 -0
- package/docs/languages.json +4 -4
- package/docs/model-options.md +8 -5
- package/docs/operator-manual-vietnamese.md +13 -11
- package/docs/project-adapters.md +2 -2
- package/docs/publishing-for-teams.md +9 -0
- package/docs/quality-benchmark.md +271 -62
- package/docs/release-install-policy.md +10 -9
- package/docs/runtime-policy-design.md +4 -4
- package/docs/runtime-quality-baseline.md +13 -6
- package/docs/security-threat-model.md +2 -2
- package/docs/task-lifecycle-vietnamese.md +2 -2
- package/docs/team-onboarding.md +2 -2
- package/docs/usage-observability.md +7 -2
- package/docs/vi/architecture.md +1 -0
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +1 -1
- 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 +1 -1
- 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 +1 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +1 -1
- 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 +1 -1
- 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 +1 -1
- package/docs-site/workflows.html +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
- package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
- package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
- 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 +2 -2
- package/evals/fs5-pilot-protocol.v4.json +6 -5
- package/evals/fs5-pilot-protocol.v5.json +7 -6
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/package.json +3 -1
- package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
- package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
- package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
- package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
- package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
- package/packages/piagent-core/benchmark/benchmark-core.js +732 -661
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
- package/packages/piagent-core/benchmark/benchmark-forensics.js +439 -4
- package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
- package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
- package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
- package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +183 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +116 -12
- package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
- package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +782 -0
- package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
- package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
- package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
- package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
- package/packages/piagent-core/benchmark/benchmark-suite.js +128 -2
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +376 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +311 -0
- package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
- package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
- package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
- package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
- package/packages/piagent-core/extensions/acceptance-receipt.js +34 -11
- package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
- package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
- package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
- package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
- package/packages/piagent-core/extensions/context-engine.js +148 -145
- package/packages/piagent-core/extensions/context-evidence.js +48 -0
- package/packages/piagent-core/extensions/context-import-links.js +296 -0
- package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +422 -7
- package/packages/piagent-core/extensions/criterion-graph.js +78 -8
- package/packages/piagent-core/extensions/guard-types.ts +3 -0
- package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +250 -43
- package/packages/piagent-core/extensions/policy-core.js +1 -1
- package/packages/piagent-core/extensions/task-contract-view.js +17 -3
- package/packages/piagent-core/extensions/task-lifecycle.js +7 -2
- package/packages/piagent-core/extensions/task-state.js +40 -134
- package/packages/piagent-core/extensions/verification-intelligence.js +3 -1
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +157 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +3 -0
- package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
- package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
- package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
- package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
- package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
- package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
- package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +5 -6
- package/packages/piagent-core/runtime/hooks/input-hook.ts +11 -1
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +7 -2
- package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +84 -102
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +9 -3
- package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
- package/packages/piagent-core/runtime/inspection/approval-broker.ts +29 -5
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +53 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +34 -6
- package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
- package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
- package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
- package/packages/piagent-core/runtime/orchestration/role-policy.ts +1 -1
- package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +4 -2
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +2 -1
- package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +6 -2
- package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
- package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
- package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +55 -2
- package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
- package/packages/piagent-core/runtime/registration/context-commands.ts +66 -5
- package/packages/piagent-core/runtime/registration/policy-tools.ts +46 -11
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +49 -16
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +163 -0
- package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +67 -62
- package/packages/piagent-core/runtime/session/runtime-state.ts +154 -2
- package/packages/piagent-core/runtime/session/system-prompt.ts +4 -4
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
- package/packages/piagent-core/runtime/workflows/task-intake.ts +43 -2
- package/packages/piagent-core/security/sensitive-data.js +13 -218
- package/packages/piagent-core/security/sensitive-project-file.js +42 -0
- package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
- package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
- package/packages/piagent-core/security/sensitive-text.js +234 -0
- package/packages/piagent-core/subagents/piagent-worker.md +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-CxeZqTte.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-DynIMe8I.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
- package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +93 -7
- package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
- package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
- package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +158 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +139 -120
- package/packages/piagent-webui/server/loopback-server.ts +8 -0
- package/packages/piagent-webui/server/transcript-projection.ts +22 -4
- package/packages/piagent-webui/shared/text-visibility.ts +10 -0
- package/schemas/piagent-webui/catalog-v1.json +9 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
- package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
- package/schemas/task-contract.schema.json +12 -1
- package/scripts/benchmark-runner-core.mjs +355 -359
- package/scripts/benchmark-runner-finalization.mjs +309 -0
- package/scripts/benchmark-runner-host-readiness.mjs +94 -0
- package/scripts/benchmark-runner-support.mjs +200 -0
- package/scripts/benchmark-session.mjs +95 -41
- package/scripts/configure-model-scope.sh +4 -3
- package/scripts/helper-evaluation.mjs +1 -1
- package/scripts/install-global.sh +2 -2
- package/scripts/pi-usage-history.mjs +37 -10
- package/scripts/piagent-dashboard.mjs +13 -1
- package/scripts/setup.sh +2 -2
- package/scripts/verify-local.sh +5 -1
- package/templates/global/settings.json +2 -1
- package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { StringDecoder } from "node:string_decoder";
|
|
2
|
+
|
|
3
|
+
const CODEX_NON_TOOL_ITEMS = new Set(["agent_message", "reasoning", "plan", "user_message"]);
|
|
4
|
+
const TOKEN_FIELDS = Object.freeze(["input", "output", "cacheRead", "cacheWrite", "reasoning", "fresh", "total"]);
|
|
5
|
+
|
|
6
|
+
export const BENCHMARK_TOKEN_DEFINITIONS = Object.freeze({
|
|
7
|
+
unit: "provider-reported-tokens",
|
|
8
|
+
input: "Fresh input tokens, excluding cache-read and cache-write input.",
|
|
9
|
+
output: "All output tokens; reasoning is a subset and is not added again.",
|
|
10
|
+
cacheRead: "Input tokens served from provider cache.",
|
|
11
|
+
cacheWrite: "Input tokens written to provider cache.",
|
|
12
|
+
reasoning: "Reasoning token subset included inside output.",
|
|
13
|
+
fresh: "Claim denominator: input + output.",
|
|
14
|
+
total: "Accounted provider tokens: input + cacheRead + cacheWrite + output."
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function plainObject(value) {
|
|
18
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function exactBenchmarkMeasuredUsage(usage) {
|
|
22
|
+
return usage?.usageCompleteness === "exact"
|
|
23
|
+
&& Number.isInteger(usage?.sessions) && usage.sessions > 0
|
|
24
|
+
&& TOKEN_FIELDS.every((field) => Number.isFinite(usage?.[field]) && usage[field] >= 0)
|
|
25
|
+
&& usage.fresh === usage.input + usage.output
|
|
26
|
+
&& usage.total === usage.input + usage.cacheRead + usage.cacheWrite + usage.output
|
|
27
|
+
&& usage.reasoning <= usage.output;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function knownPreProviderZero(usage, status) {
|
|
31
|
+
return status === "known-pre-provider-zero"
|
|
32
|
+
&& Number(usage?.sessions ?? 0) === 0
|
|
33
|
+
&& TOKEN_FIELDS.every((field) => Number(usage?.[field] ?? 0) === 0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function exactBenchmarkAttemptUsage(usage, status) {
|
|
37
|
+
return status !== "unknown-after-provider-start"
|
|
38
|
+
&& (exactBenchmarkMeasuredUsage(usage) || knownPreProviderZero(usage, status));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function tokenBucket(entries) {
|
|
42
|
+
const totals = Object.fromEntries(TOKEN_FIELDS.map((field) => [field, 0]));
|
|
43
|
+
const bySurface = {};
|
|
44
|
+
let exactAttempts = 0;
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
const exact = exactBenchmarkAttemptUsage(entry.usage, entry.status);
|
|
47
|
+
if (!exact) continue;
|
|
48
|
+
exactAttempts += 1;
|
|
49
|
+
const surface = bySurface[entry.surface] ?? { attempts: 0, tokens: Object.fromEntries(TOKEN_FIELDS.map((field) => [field, 0])) };
|
|
50
|
+
surface.attempts += 1;
|
|
51
|
+
for (const field of TOKEN_FIELDS) {
|
|
52
|
+
const value = Number(entry.usage?.[field] ?? 0);
|
|
53
|
+
totals[field] += value;
|
|
54
|
+
surface.tokens[field] += value;
|
|
55
|
+
}
|
|
56
|
+
bySurface[entry.surface] = surface;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
attempts: entries.length,
|
|
60
|
+
exactAttempts,
|
|
61
|
+
unknownAttempts: entries.length - exactAttempts,
|
|
62
|
+
complete: exactAttempts === entries.length,
|
|
63
|
+
tokens: totals,
|
|
64
|
+
bySurface: Object.fromEntries(Object.entries(bySurface).sort(([left], [right]) => left.localeCompare(right)))
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function benchmarkTokenAccounting(runs) {
|
|
69
|
+
const accepted = runs.map((run) => ({ surface: run.surface, status: run.usageStatus ?? "measured", usage: run.usage }));
|
|
70
|
+
const failed = runs.flatMap((run) => (run.infrastructureFailures ?? []).map((attempt) => ({
|
|
71
|
+
surface: run.surface,
|
|
72
|
+
status: attempt.usageStatus ?? "unknown-after-provider-start",
|
|
73
|
+
usage: attempt.usage
|
|
74
|
+
})));
|
|
75
|
+
return {
|
|
76
|
+
schemaVersion: 1,
|
|
77
|
+
definitions: BENCHMARK_TOKEN_DEFINITIONS,
|
|
78
|
+
acceptedAttempts: tokenBucket(accepted),
|
|
79
|
+
failedAttempts: tokenBucket(failed),
|
|
80
|
+
allAttempts: tokenBucket([...accepted, ...failed])
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function benchmarkInfrastructureFailureLedgerIssues(runs) {
|
|
85
|
+
return runs.flatMap((run) => {
|
|
86
|
+
const issues = [];
|
|
87
|
+
if (!Number.isInteger(run.infrastructureRetries) || run.infrastructureRetries < 0) {
|
|
88
|
+
issues.push("invalid-infrastructure-retry-count");
|
|
89
|
+
}
|
|
90
|
+
if (!Array.isArray(run.infrastructureFailures)) {
|
|
91
|
+
issues.push("missing-infrastructure-failure-ledger");
|
|
92
|
+
} else if (Number.isInteger(run.infrastructureRetries) && run.infrastructureFailures.length !== run.infrastructureRetries) {
|
|
93
|
+
issues.push("retry-ledger-count-mismatch");
|
|
94
|
+
}
|
|
95
|
+
if (!Number.isInteger(run.infrastructureAttempts)
|
|
96
|
+
|| run.infrastructureAttempts !== (Number.isInteger(run.infrastructureRetries) ? run.infrastructureRetries + 1 : -1)) {
|
|
97
|
+
issues.push("attempt-ledger-count-mismatch");
|
|
98
|
+
}
|
|
99
|
+
return issues.length > 0 ? [{
|
|
100
|
+
scenarioId: run.scenarioId ?? null,
|
|
101
|
+
surface: run.surface ?? null,
|
|
102
|
+
repeat: run.repeat ?? null,
|
|
103
|
+
issues
|
|
104
|
+
}] : [];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function aggregateSessionUsage(sessions) {
|
|
109
|
+
const totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0, total: 0, cost: 0 };
|
|
110
|
+
const models = new Set();
|
|
111
|
+
const thinkingLevels = new Set();
|
|
112
|
+
const toolNames = {};
|
|
113
|
+
let toolCalls = 0;
|
|
114
|
+
let messages = 0;
|
|
115
|
+
const contextSnapshots = [];
|
|
116
|
+
for (const session of sessions) {
|
|
117
|
+
for (const key of Object.keys(totals)) totals[key] += Number(session.tokens?.[key] ?? 0);
|
|
118
|
+
if (session.modelId || session.provider) models.add(`${session.provider || "unknown"}/${session.modelId || "unknown"}`);
|
|
119
|
+
if (session.thinkingLevel) thinkingLevels.add(session.thinkingLevel);
|
|
120
|
+
toolCalls += Number(session.messages?.toolCalls ?? 0);
|
|
121
|
+
messages += Number(session.messages?.total ?? 0);
|
|
122
|
+
if (plainObject(session.contextUsage)) {
|
|
123
|
+
const snapshot = {
|
|
124
|
+
tokens: Number.isFinite(session.contextUsage.tokens) ? session.contextUsage.tokens : null,
|
|
125
|
+
contextWindow: Number.isFinite(session.contextUsage.contextWindow) ? session.contextUsage.contextWindow : null,
|
|
126
|
+
percent: Number.isFinite(session.contextUsage.percent) ? session.contextUsage.percent : null
|
|
127
|
+
};
|
|
128
|
+
if (Object.values(snapshot).some(Number.isFinite)) contextSnapshots.push(snapshot);
|
|
129
|
+
}
|
|
130
|
+
for (const [name, count] of Object.entries(session.toolNames ?? {})) {
|
|
131
|
+
toolNames[name] = (toolNames[name] ?? 0) + Number(count ?? 0);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
...totals,
|
|
136
|
+
fresh: totals.input + totals.output,
|
|
137
|
+
sessions: sessions.length,
|
|
138
|
+
subagentSessions: sessions.filter((session) => session.isSubagent).length,
|
|
139
|
+
toolCalls,
|
|
140
|
+
toolNames: Object.fromEntries(Object.entries(toolNames).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))),
|
|
141
|
+
messages,
|
|
142
|
+
model: models.size === 1 ? [...models][0] : models.size === 0 ? "unknown" : "mixed",
|
|
143
|
+
thinkingLevel: thinkingLevels.size === 1 ? [...thinkingLevels][0] : thinkingLevels.size === 0 ? "unknown" : "mixed",
|
|
144
|
+
usageSource: sessions.length > 0 ? "pi-session-jsonl" : "unavailable",
|
|
145
|
+
usageCompleteness: sessions.length > 0 && sessions.every((session) => session.usageIntegrity?.exact === true) ? "exact" : "unverified",
|
|
146
|
+
contextUsage: contextSnapshots.length > 0 ? {
|
|
147
|
+
source: "session-reported",
|
|
148
|
+
observations: contextSnapshots.length,
|
|
149
|
+
peakTokens: contextSnapshots.some((item) => Number.isFinite(item.tokens))
|
|
150
|
+
? Math.max(...contextSnapshots.map((item) => item.tokens).filter(Number.isFinite))
|
|
151
|
+
: null,
|
|
152
|
+
contextWindow: new Set(contextSnapshots.map((item) => item.contextWindow).filter(Number.isFinite)).size === 1
|
|
153
|
+
? contextSnapshots.map((item) => item.contextWindow).find(Number.isFinite)
|
|
154
|
+
: null,
|
|
155
|
+
peakPercent: contextSnapshots.some((item) => Number.isFinite(item.percent))
|
|
156
|
+
? Math.max(...contextSnapshots.map((item) => item.percent).filter(Number.isFinite))
|
|
157
|
+
: null
|
|
158
|
+
} : {
|
|
159
|
+
source: "unavailable",
|
|
160
|
+
observations: 0,
|
|
161
|
+
peakTokens: null,
|
|
162
|
+
contextWindow: null,
|
|
163
|
+
peakPercent: null
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function requiredCodexToken(value, field) {
|
|
169
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
170
|
+
throw new Error(`Codex JSONL usage.${field} must be a non-negative integer`);
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function consumeCodexEvent(state, event, lineNumber) {
|
|
176
|
+
if (!plainObject(event) || typeof event.type !== "string") {
|
|
177
|
+
throw new Error(`Codex JSONL line ${lineNumber} is not an event object`);
|
|
178
|
+
}
|
|
179
|
+
state.onEvent?.(event);
|
|
180
|
+
if (event.type === "error" || event.type === "turn.failed") {
|
|
181
|
+
const nested = plainObject(event.error) ? event.error : {};
|
|
182
|
+
const message = [event.message, nested.message, nested.additional_details]
|
|
183
|
+
.find((value) => typeof value === "string" && value.trim());
|
|
184
|
+
state.diagnostics.push({ type: event.type, message: message ? message.trim().slice(0, 2_000) : "unspecified Codex error" });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (event.type === "thread.started" && typeof event.thread_id === "string" && event.thread_id) {
|
|
188
|
+
state.threadId = event.thread_id;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (event.type === "item.completed" && plainObject(event.item)) {
|
|
192
|
+
const type = event.item.type;
|
|
193
|
+
if (type === "agent_message") state.messages += 1;
|
|
194
|
+
else if (typeof type === "string" && type && !CODEX_NON_TOOL_ITEMS.has(type)) {
|
|
195
|
+
state.toolNames[type] = (state.toolNames[type] ?? 0) + 1;
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (event.type === "turn.completed") {
|
|
200
|
+
state.completedTurns += 1;
|
|
201
|
+
if (state.completedTurns > 1) throw new Error("Codex JSONL contains more than one completed root turn");
|
|
202
|
+
if (!plainObject(event.usage)) throw new Error("Codex JSONL turn.completed is missing usage");
|
|
203
|
+
state.completedUsage = event.usage;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function finishCodexUsage(state) {
|
|
208
|
+
if (!state.threadId) throw new Error("Codex JSONL is missing thread.started");
|
|
209
|
+
if (!state.completedUsage) throw new Error("Codex JSONL is missing turn.completed usage");
|
|
210
|
+
const providerInput = requiredCodexToken(state.completedUsage.input_tokens, "input_tokens");
|
|
211
|
+
const cacheRead = requiredCodexToken(state.completedUsage.cached_input_tokens ?? 0, "cached_input_tokens");
|
|
212
|
+
const cacheWrite = requiredCodexToken(state.completedUsage.cache_write_input_tokens ?? 0, "cache_write_input_tokens");
|
|
213
|
+
const output = requiredCodexToken(state.completedUsage.output_tokens, "output_tokens");
|
|
214
|
+
const reasoning = requiredCodexToken(state.completedUsage.reasoning_output_tokens ?? 0, "reasoning_output_tokens");
|
|
215
|
+
if (cacheRead + cacheWrite > providerInput) throw new Error("Codex JSONL cached and cache-write input tokens exceed input_tokens");
|
|
216
|
+
if (reasoning > output) throw new Error("Codex JSONL reasoning_output_tokens exceeds output_tokens");
|
|
217
|
+
const input = providerInput - cacheRead - cacheWrite;
|
|
218
|
+
const sortedTools = Object.fromEntries(Object.entries(state.toolNames).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])));
|
|
219
|
+
return {
|
|
220
|
+
input,
|
|
221
|
+
providerInput,
|
|
222
|
+
output,
|
|
223
|
+
cacheRead,
|
|
224
|
+
cacheWrite,
|
|
225
|
+
reasoning,
|
|
226
|
+
total: providerInput + output,
|
|
227
|
+
fresh: input + output,
|
|
228
|
+
cost: null,
|
|
229
|
+
costSource: "unavailable",
|
|
230
|
+
usageSource: "codex-turn-completed",
|
|
231
|
+
usageCompleteness: "exact",
|
|
232
|
+
sessions: 1,
|
|
233
|
+
subagentSessions: 0,
|
|
234
|
+
toolCalls: Object.values(sortedTools).reduce((sum, value) => sum + value, 0),
|
|
235
|
+
toolNames: sortedTools,
|
|
236
|
+
messages: state.messages,
|
|
237
|
+
model: typeof state.model === "string" && state.model ? state.model : "unknown",
|
|
238
|
+
thinkingLevel: typeof state.thinkingLevel === "string" && state.thinkingLevel ? state.thinkingLevel : "unknown",
|
|
239
|
+
contextUsage: {
|
|
240
|
+
source: "unavailable",
|
|
241
|
+
observations: 0,
|
|
242
|
+
peakTokens: null,
|
|
243
|
+
contextWindow: null,
|
|
244
|
+
peakPercent: null
|
|
245
|
+
},
|
|
246
|
+
providerSessionId: state.threadId
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function createCodexExecJsonlCollector(options = {}) {
|
|
251
|
+
const state = {
|
|
252
|
+
model: options.model,
|
|
253
|
+
thinkingLevel: options.thinkingLevel,
|
|
254
|
+
onEvent: typeof options.onEvent === "function" ? options.onEvent : undefined,
|
|
255
|
+
threadId: undefined,
|
|
256
|
+
completedUsage: undefined,
|
|
257
|
+
completedTurns: 0,
|
|
258
|
+
messages: 0,
|
|
259
|
+
toolNames: {},
|
|
260
|
+
diagnostics: []
|
|
261
|
+
};
|
|
262
|
+
let buffer = "";
|
|
263
|
+
let lineNumber = 0;
|
|
264
|
+
let failure;
|
|
265
|
+
const decoder = new StringDecoder("utf8");
|
|
266
|
+
const consumeLine = (rawLine) => {
|
|
267
|
+
lineNumber += 1;
|
|
268
|
+
const line = rawLine.trim();
|
|
269
|
+
if (!line) return;
|
|
270
|
+
let event;
|
|
271
|
+
try { event = JSON.parse(line); }
|
|
272
|
+
catch { throw new Error(`Codex JSONL line ${lineNumber} is not valid JSON`); }
|
|
273
|
+
consumeCodexEvent(state, event, lineNumber);
|
|
274
|
+
};
|
|
275
|
+
return {
|
|
276
|
+
write(chunk) {
|
|
277
|
+
if (failure) return;
|
|
278
|
+
try {
|
|
279
|
+
buffer += typeof chunk === "string" ? chunk : decoder.write(Buffer.from(chunk));
|
|
280
|
+
let newline;
|
|
281
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
282
|
+
consumeLine(buffer.slice(0, newline));
|
|
283
|
+
buffer = buffer.slice(newline + 1);
|
|
284
|
+
}
|
|
285
|
+
if (buffer.length > 64 * 1024 * 1024) throw new Error("Codex JSONL event exceeds the 64 MiB safety limit");
|
|
286
|
+
} catch (error) {
|
|
287
|
+
failure = error;
|
|
288
|
+
buffer = "";
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
finish() {
|
|
292
|
+
if (!failure) buffer += decoder.end();
|
|
293
|
+
if (!failure && buffer) {
|
|
294
|
+
try { consumeLine(buffer); } catch (error) { failure = error; }
|
|
295
|
+
buffer = "";
|
|
296
|
+
}
|
|
297
|
+
if (failure) throw failure;
|
|
298
|
+
return finishCodexUsage(state);
|
|
299
|
+
},
|
|
300
|
+
diagnostics() {
|
|
301
|
+
return state.diagnostics.map((item) => ({ ...item }));
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function parseCodexExecJsonl(stdout, options = {}) {
|
|
307
|
+
if (typeof stdout !== "string") throw new Error("Codex JSONL output must be a string");
|
|
308
|
+
const collector = createCodexExecJsonlCollector(options);
|
|
309
|
+
collector.write(stdout);
|
|
310
|
+
return collector.finish();
|
|
311
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { rounded } from "./benchmark-statistics.js";
|
|
2
|
+
import {
|
|
3
|
+
isCurrentTaskWorkingTreeDigest,
|
|
4
|
+
taskWorkingTreeEvidenceDigest,
|
|
5
|
+
taskWorkingTreeSnapshotUsesCurrentAlgorithm
|
|
6
|
+
} from "./benchmark-tree-identity.js";
|
|
7
|
+
|
|
8
|
+
function plainObject(value) {
|
|
9
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function meaningfulVerifyCommands(commands) {
|
|
13
|
+
if (!Array.isArray(commands)) return [];
|
|
14
|
+
return commands.filter((command) => typeof command === "string" && command.trim() && !/^(?:true|:|echo\b|printf\b)/i.test(command.trim()));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function latestObservedTaskEvidence(evidence, command) {
|
|
18
|
+
let latest, latestTime = Number.NEGATIVE_INFINITY, latestIndex = -1;
|
|
19
|
+
for (const [index, item] of (Array.isArray(evidence) ? evidence : []).entries()) {
|
|
20
|
+
if (item?.observed !== true || item.command?.trim() !== command) continue;
|
|
21
|
+
const observedAt = item.observedAt ?? item.recordedAt;
|
|
22
|
+
const parsed = typeof observedAt === "string" ? Date.parse(observedAt) : Number.NaN;
|
|
23
|
+
const time = Number.isFinite(parsed) ? parsed : Number.POSITIVE_INFINITY;
|
|
24
|
+
if (time > latestTime || (time === latestTime && index > latestIndex)) {
|
|
25
|
+
latest = Number.isFinite(parsed) ? item : null;
|
|
26
|
+
latestTime = time;
|
|
27
|
+
latestIndex = index;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return latest;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function stableTaskVerifierEvidence(item) {
|
|
34
|
+
return item?.exitCode === 0
|
|
35
|
+
&& item.matchedProfileCommand === true
|
|
36
|
+
&& isCurrentTaskWorkingTreeDigest(item.preWorkingTreeDigest)
|
|
37
|
+
&& item.preWorkingTreeDigest === item.workingTreeDigest;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function semanticAcceptanceEvidenceRequired(task) {
|
|
41
|
+
const snapshot = task?.authoritySnapshot;
|
|
42
|
+
if (!plainObject(snapshot)
|
|
43
|
+
|| snapshot.taskId !== task?.taskId
|
|
44
|
+
|| snapshot.taskRunId !== task?.taskRunId
|
|
45
|
+
|| snapshot.capturedAt !== task?.createdAt) return true;
|
|
46
|
+
const semantic = Array.isArray(snapshot.capabilities)
|
|
47
|
+
? snapshot.capabilities.find((entry) => entry?.id === "CAP-13")
|
|
48
|
+
: undefined;
|
|
49
|
+
return !semantic || semantic.authority === "enforce" || semantic.authority === "orchestrate";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const RUNTIME_MANAGED_BENCHMARK_TOOLS = new Set([
|
|
53
|
+
"piagent_context",
|
|
54
|
+
"piagent_context_preflight",
|
|
55
|
+
"piagent_context_engine",
|
|
56
|
+
"piagent_context_budget",
|
|
57
|
+
"piagent_context_index_status",
|
|
58
|
+
"piagent_context_index_search",
|
|
59
|
+
"piagent_context_record",
|
|
60
|
+
"piagent_permission_status",
|
|
61
|
+
"piagent_exec_policy_check",
|
|
62
|
+
"piagent_tool_policy_check",
|
|
63
|
+
"piagent_verify_record",
|
|
64
|
+
"piagent_trace_record",
|
|
65
|
+
"piagent_task_gate_check",
|
|
66
|
+
"piagent_tools"
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
export function evaluateWorkflowEvidence(task, changedFiles, toolNames = {}, options = {}) {
|
|
70
|
+
const scenarioKind = options.scenarioKind ?? "source-change";
|
|
71
|
+
const planned = meaningfulVerifyCommands(task?.verifyCommands);
|
|
72
|
+
const verifyEvidence = Array.isArray(task?.verifyEvidence) ? task.verifyEvidence : [];
|
|
73
|
+
const latestConfiguredEvidence = planned.map((command) => latestObservedTaskEvidence(verifyEvidence, command));
|
|
74
|
+
const terminalVerifierDigests = new Set(latestConfiguredEvidence
|
|
75
|
+
.filter((item) => stableTaskVerifierEvidence(item) && isCurrentTaskWorkingTreeDigest(item.workingTreeDigest))
|
|
76
|
+
.map((item) => item.workingTreeDigest));
|
|
77
|
+
const terminalVerifierDigest = latestConfiguredEvidence.length === planned.length
|
|
78
|
+
&& latestConfiguredEvidence.every(stableTaskVerifierEvidence)
|
|
79
|
+
&& latestConfiguredEvidence.every((item) => isCurrentTaskWorkingTreeDigest(item?.workingTreeDigest))
|
|
80
|
+
&& terminalVerifierDigests.size === 1
|
|
81
|
+
? [...terminalVerifierDigests][0]
|
|
82
|
+
: undefined;
|
|
83
|
+
const passed = new Set(latestConfiguredEvidence
|
|
84
|
+
.filter((item) => terminalVerifierDigest && item?.exitCode === 0 && item.workingTreeDigest === terminalVerifierDigest)
|
|
85
|
+
.map((item) => item.command?.trim()));
|
|
86
|
+
const actual = [...new Set(changedFiles ?? [])].sort();
|
|
87
|
+
const claimed = [...new Set(task?.changedFiles ?? [])].sort();
|
|
88
|
+
const baselineFiles = Object.keys(plainObject(task?.baselineFileDigests) ? task.baselineFileDigests : {}).sort();
|
|
89
|
+
const finalFiles = Object.keys(plainObject(task?.finalFileDigests) ? task.finalFileDigests : {}).sort();
|
|
90
|
+
const baselineFileClaims = Array.isArray(task?.baselineChangedFiles) ? [...task.baselineChangedFiles].sort() : [];
|
|
91
|
+
const finalFileClaims = Array.isArray(task?.finalWorkingTreeFiles) ? [...task.finalWorkingTreeFiles].sort() : [];
|
|
92
|
+
const migrationReady = task?.workingTreeDigestMigration === undefined;
|
|
93
|
+
const rootTreeContractCurrent = task?.workingTreeDigestAlgorithm === "wt-content-v2"
|
|
94
|
+
&& migrationReady
|
|
95
|
+
&& Array.isArray(task?.baselineChangedFiles)
|
|
96
|
+
&& Array.isArray(task?.finalWorkingTreeFiles)
|
|
97
|
+
&& taskWorkingTreeSnapshotUsesCurrentAlgorithm(task?.baselineFileDigests)
|
|
98
|
+
&& taskWorkingTreeSnapshotUsesCurrentAlgorithm(task?.finalFileDigests)
|
|
99
|
+
&& JSON.stringify(baselineFiles) === JSON.stringify(baselineFileClaims)
|
|
100
|
+
&& JSON.stringify(finalFiles) === JSON.stringify(finalFileClaims)
|
|
101
|
+
&& JSON.stringify(finalFiles) === JSON.stringify(actual);
|
|
102
|
+
const finalTreeDigest = rootTreeContractCurrent ? taskWorkingTreeEvidenceDigest(task.finalFileDigests) : undefined;
|
|
103
|
+
const currentTreeEvidence = rootTreeContractCurrent
|
|
104
|
+
&& (scenarioKind === "read-only" || (Boolean(terminalVerifierDigest) && terminalVerifierDigest === finalTreeDigest));
|
|
105
|
+
const taskStartCalls = Number(toolNames?.piagent_task_start ?? 0);
|
|
106
|
+
const acceptedTaskStartCount = Number.isInteger(options.acceptedTaskStartCount)
|
|
107
|
+
? Math.max(0, options.acceptedTaskStartCount)
|
|
108
|
+
: task?.taskRunId ? 1 : 0;
|
|
109
|
+
const intakeMode = task?.intakeMode === "runtime" ? "runtime" : "model";
|
|
110
|
+
const acceptanceCriteria = Array.isArray(task?.acceptanceReceipt?.criteria) ? task.acceptanceReceipt.criteria : [];
|
|
111
|
+
const criticalAcceptance = acceptanceCriteria.filter((criterion) => criterion?.priority === "critical");
|
|
112
|
+
const requireSemanticAcceptanceEvidence = semanticAcceptanceEvidenceRequired(task);
|
|
113
|
+
const runtimeManagedCalls = Object.entries(toolNames ?? {})
|
|
114
|
+
.filter(([name]) => RUNTIME_MANAGED_BENCHMARK_TOOLS.has(name))
|
|
115
|
+
.reduce((sum, [, count]) => sum + Number(count ?? 0), 0);
|
|
116
|
+
const checks = [
|
|
117
|
+
{ id: "session-bound-task", passed: Boolean(task?.schemaVersion === 2 && task.taskRunId && task.sessionId), weight: 1 },
|
|
118
|
+
{ id: "terminal-completion", passed: task?.trace?.outcome === "completed", weight: 1 },
|
|
119
|
+
{ id: "completed-work-plan", passed: Array.isArray(task?.workPlan) && task.workPlan.length > 0 && task.workPlan.every((step) => ["done", "skipped"].includes(step.status)), weight: 1 },
|
|
120
|
+
{ id: "current-tree-evidence", passed: currentTreeEvidence, weight: 1 },
|
|
121
|
+
scenarioKind === "read-only"
|
|
122
|
+
? { id: "truthful-no-changes", passed: actual.length === 0 && claimed.length === 0, weight: 1 }
|
|
123
|
+
: { id: "observed-verification", passed: planned.length > 0 && planned.every((command) => passed.has(command.trim())), weight: 1 },
|
|
124
|
+
...(scenarioKind === "read-only" ? [] : [
|
|
125
|
+
{ id: "truthful-changed-files", passed: actual.length > 0 && JSON.stringify(actual) === JSON.stringify(claimed), weight: 1 }
|
|
126
|
+
]),
|
|
127
|
+
// Keep advisory semantic coverage visible for every source-change run, but
|
|
128
|
+
// do not let a finite proof classifier override an independently graded
|
|
129
|
+
// correct outcome when CAP-13 is not part of the task's authority. Strict
|
|
130
|
+
// profiles retain the full workflow weight.
|
|
131
|
+
...(acceptanceCriteria.length > 0 && (scenarioKind === "source-change" || requireSemanticAcceptanceEvidence) ? [
|
|
132
|
+
{
|
|
133
|
+
id: "criterion-linked-evidence",
|
|
134
|
+
passed: (criticalAcceptance.length ? criticalAcceptance : acceptanceCriteria).every((criterion) => criterion.status === "satisfied"
|
|
135
|
+
&& Array.isArray(criterion.evidence)
|
|
136
|
+
&& (scenarioKind === "read-only"
|
|
137
|
+
? criterion.evidence.length > 0
|
|
138
|
+
: Boolean(terminalVerifierDigest) && criterion.evidence.some((evidence) => evidence?.workingTreeDigest === terminalVerifierDigest))),
|
|
139
|
+
weight: requireSemanticAcceptanceEvidence ? 1 : 0.25
|
|
140
|
+
}
|
|
141
|
+
] : []),
|
|
142
|
+
{
|
|
143
|
+
id: "single-task-start",
|
|
144
|
+
passed: acceptedTaskStartCount === 1,
|
|
145
|
+
weight: 1
|
|
146
|
+
},
|
|
147
|
+
{ id: "runtime-managed-evidence", passed: runtimeManagedCalls === 0, weight: 1 }
|
|
148
|
+
];
|
|
149
|
+
const earned = checks.reduce((sum, check) => sum + (check.passed ? check.weight : 0), 0);
|
|
150
|
+
const available = checks.reduce((sum, check) => sum + check.weight, 0);
|
|
151
|
+
return {
|
|
152
|
+
score: rounded(10 * earned / available, 2),
|
|
153
|
+
checks,
|
|
154
|
+
choreography: { intakeMode, taskStartCalls, acceptedTaskStartCount, runtimeManagedCalls },
|
|
155
|
+
taskEvidence: {
|
|
156
|
+
outcome: task?.trace?.outcome ?? "missing",
|
|
157
|
+
acceptance: {
|
|
158
|
+
criteria: acceptanceCriteria.length,
|
|
159
|
+
satisfied: acceptanceCriteria.filter((criterion) => criterion?.status === "satisfied").length,
|
|
160
|
+
critical: criticalAcceptance.length,
|
|
161
|
+
criticalSatisfied: criticalAcceptance.filter((criterion) => criterion?.status === "satisfied").length
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
scenarioKind
|
|
165
|
+
};
|
|
166
|
+
}
|