@hassangameryt/freeflow 0.8.0 → 0.9.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/.agents/plugins/marketplace.json +2 -2
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +3 -3
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +96 -149
- package/capabilities/cognitive-routing/SKILL.md +21 -129
- package/capabilities/cognitive-routing/references/coordinator.md +163 -0
- package/capabilities/cognitive-routing/references/worker.md +38 -0
- package/capabilities/compaction/SKILL.md +3 -3
- package/capabilities/compaction/references/summary-format.md +26 -15
- package/capabilities/messaging/SKILL.md +49 -0
- package/capabilities/task-memory/SKILL.md +87 -0
- package/command-surface.json +6 -54
- package/gemini-extension.json +1 -1
- package/hooks/claude/hooks.json +13 -3
- package/hooks/codex/hooks.json +2 -1
- package/hooks/shared/runtime-context.mjs +69 -77
- package/package.json +25 -8
- package/pi-extension/dist/ask-user/ask.js +108 -0
- package/pi-extension/dist/ask-user/form.js +250 -0
- package/pi-extension/dist/ask-user/questions.js +66 -0
- package/pi-extension/dist/ask-user/tool.js +124 -0
- package/pi-extension/dist/cognitive-routing/assembler.js +35 -11
- package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
- package/pi-extension/dist/cognitive-routing/config.js +3 -4
- package/pi-extension/dist/cognitive-routing/event-store.js +4 -1
- package/pi-extension/dist/cognitive-routing/gate.js +20 -5
- package/pi-extension/dist/cognitive-routing/handlers.js +15 -13
- package/pi-extension/dist/cognitive-routing/model-control.js +17 -8
- package/pi-extension/dist/cognitive-routing/projection.js +23 -6
- package/pi-extension/dist/cognitive-routing/provenance.js +3 -1
- package/pi-extension/dist/cognitive-routing/render.js +15 -15
- package/pi-extension/dist/cognitive-routing/runtime.js +4 -2
- package/pi-extension/dist/cognitive-routing/schemas.js +4 -4
- package/pi-extension/dist/cognitive-routing/session.js +3 -1
- package/pi-extension/dist/cognitive-routing/sources.js +29 -4
- package/pi-extension/dist/cognitive-routing/state.js +18 -8
- package/pi-extension/dist/cognitive-routing/status.js +4 -3
- package/pi-extension/dist/cognitive-routing/tools.js +29 -15
- package/pi-extension/dist/cognitive-routing/types.js +10 -1
- package/pi-extension/dist/compaction/carry.js +36 -22
- package/pi-extension/dist/compaction/config.js +4 -2
- package/pi-extension/dist/compaction/controller.js +52 -29
- package/pi-extension/dist/compaction/harness.js +19 -3
- package/pi-extension/dist/compaction/results.js +14 -13
- package/pi-extension/dist/compaction/tool.js +6 -6
- package/pi-extension/dist/host/catalog.js +20 -35
- package/pi-extension/dist/host/config-validation.js +233 -0
- package/pi-extension/dist/host/config.js +78 -91
- package/pi-extension/dist/host/inbound.js +142 -0
- package/pi-extension/dist/host/projection-tags.js +1 -1
- package/pi-extension/dist/host/prompts.js +24 -13
- package/pi-extension/dist/host/read-only-session.js +6 -1
- package/pi-extension/dist/host/request-history.js +7 -3
- package/pi-extension/dist/host/rows.js +25 -0
- package/pi-extension/dist/host/runtime-state.js +17 -2
- package/pi-extension/dist/host/section-keeper.js +85 -0
- package/pi-extension/dist/host/settings/freeflow-command.js +846 -93
- package/pi-extension/dist/host/settings/settings-view.js +27 -8
- package/pi-extension/dist/host/status.js +19 -5
- package/pi-extension/dist/index.js +638 -72
- package/pi-extension/dist/messaging/config.js +27 -0
- package/pi-extension/dist/messaging/messaging.js +398 -0
- package/pi-extension/dist/messaging/registry.js +57 -0
- package/pi-extension/dist/messaging/render.js +90 -0
- package/pi-extension/dist/messaging/tools.js +109 -0
- package/pi-extension/dist/messaging/transport.js +101 -0
- package/pi-extension/dist/provider-support/cache/keepalive.js +16 -4
- package/pi-extension/dist/task-memory/config.js +49 -0
- package/pi-extension/dist/task-memory/registry.js +119 -0
- package/pi-extension/dist/task-memory/render.js +107 -0
- package/pi-extension/dist/task-memory/runtime.js +994 -0
- package/pi-extension/dist/task-memory/tool.js +233 -0
- package/pi-extension/dist/tool-execution/apply-patch/tool.js +1 -1
- package/pi-extension/dist/tool-execution/background.js +220 -28
- package/pi-extension/dist/tool-execution/config.js +14 -3
- package/pi-extension/dist/tool-execution/file-tracking.js +24 -4
- package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
- package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
- package/pi-extension/dist/tool-execution/tools.js +2 -2
- package/runtime/prompts/cognitive-routing.md +4 -4
- package/runtime/prompts/core.md +24 -14
- package/runtime/prompts/interaction-contract.md +7 -4
- package/runtime/prompts/tool-execution.md +3 -16
- package/runtime/prompts/working-method.md +16 -99
- package/skills/deliver-work/SKILL.md +76 -0
- package/skills/deliver-work/references/branch-closeout.md +134 -0
- package/skills/deliver-work/references/commit.md +83 -0
- package/skills/deliver-work/references/launch.md +94 -0
- package/skills/deliver-work/references/release.md +85 -0
- package/skills/deliver-work/scripts/commit-state.mjs +139 -0
- package/skills/deliver-work/scripts/delivery-state.mjs +107 -0
- package/skills/deliver-work/scripts/lib/git-state.mjs +178 -0
- package/skills/deliver-work/scripts/push-route.mjs +122 -0
- package/skills/deliver-work/scripts/release-preflight.mjs +177 -0
- package/skills/discuss-work/SKILL.md +148 -0
- package/skills/{discuss → discuss-work}/references/discussion-continuity.md +10 -27
- package/skills/discuss-work/references/learning-action.md +36 -0
- package/skills/discuss-work/scripts/choices.mjs +101 -0
- package/skills/discuss-work/scripts/lib/choices.mjs +159 -0
- package/skills/evaluate-behavior/SKILL.md +107 -0
- package/skills/evaluate-behavior/references/authoring.md +381 -0
- package/skills/evaluate-behavior/references/coding-tasks.md +145 -0
- package/skills/evaluate-behavior/references/conversations-pipelines-judges.md +177 -0
- package/skills/evaluate-behavior/references/design-and-measurement.md +273 -0
- package/skills/evaluate-behavior/references/running-and-evidence.md +245 -0
- package/skills/evaluate-behavior/scripts/claude-guard.mjs +76 -0
- package/skills/evaluate-behavior/scripts/guard-rules.mjs +309 -0
- package/skills/evaluate-behavior/scripts/lib/agreement.mjs +90 -0
- package/skills/evaluate-behavior/scripts/lib/arms.mjs +55 -0
- package/skills/evaluate-behavior/scripts/lib/budget.mjs +87 -0
- package/skills/evaluate-behavior/scripts/lib/build-source.mjs +287 -0
- package/skills/evaluate-behavior/scripts/lib/clear.mjs +74 -0
- package/skills/evaluate-behavior/scripts/lib/container.mjs +395 -0
- package/skills/evaluate-behavior/scripts/lib/decision.mjs +418 -0
- package/skills/evaluate-behavior/scripts/lib/definitions.mjs +1072 -0
- package/skills/evaluate-behavior/scripts/lib/freeflow.mjs +103 -0
- package/skills/evaluate-behavior/scripts/lib/grade-command.mjs +591 -0
- package/skills/evaluate-behavior/scripts/lib/grade-comparison.mjs +90 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-interaction.mjs +19 -18
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-support.mjs +32 -5
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-workspace.mjs +28 -12
- package/skills/evaluate-behavior/scripts/lib/grade.mjs +101 -0
- package/skills/evaluate-behavior/scripts/lib/hooks.mjs +98 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/confinement.mjs +196 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/constraints.mjs +111 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/index.mjs +48 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/isolation.mjs +74 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/launch.mjs +123 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/model-call.mjs +73 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/recorder.mjs +178 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/session.mjs +494 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/skills.mjs +81 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/index.mjs +31 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/names.mjs +3 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/confinement.mjs +173 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/host.mjs +148 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/index.mjs +70 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/isolation.mjs +81 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/model-call.mjs +79 -0
- package/skills/evaluate-behavior/scripts/lib/hosts/pi/process.mjs +241 -0
- package/skills/{evaluate-skill/scripts/lib → evaluate-behavior/scripts/lib/hosts/pi}/rpc.mjs +144 -26
- package/skills/evaluate-behavior/scripts/lib/identity.mjs +82 -0
- package/skills/evaluate-behavior/scripts/lib/judge.mjs +688 -0
- package/skills/evaluate-behavior/scripts/lib/metrics.mjs +332 -0
- package/skills/evaluate-behavior/scripts/lib/model-call.mjs +17 -0
- package/skills/evaluate-behavior/scripts/lib/participants.mjs +616 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/process.mjs +22 -0
- package/skills/evaluate-behavior/scripts/lib/queue.mjs +54 -0
- package/skills/evaluate-behavior/scripts/lib/reference.mjs +343 -0
- package/skills/evaluate-behavior/scripts/lib/reuse.mjs +277 -0
- package/skills/evaluate-behavior/scripts/lib/review.mjs +933 -0
- package/skills/evaluate-behavior/scripts/lib/runner.mjs +732 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/sandbox.mjs +119 -14
- package/skills/evaluate-behavior/scripts/lib/scan-shell.mjs +457 -0
- package/skills/evaluate-behavior/scripts/lib/scan.mjs +479 -0
- package/skills/evaluate-behavior/scripts/lib/seal.mjs +91 -0
- package/skills/evaluate-behavior/scripts/lib/shell.mjs +560 -0
- package/skills/evaluate-behavior/scripts/lib/source-override.mjs +64 -0
- package/skills/evaluate-behavior/scripts/lib/subject-file.mjs +88 -0
- package/skills/{evaluate-skill/scripts/lib/pi.mjs → evaluate-behavior/scripts/lib/subject.mjs} +235 -265
- package/skills/evaluate-behavior/scripts/lib/task-copy.mjs +124 -0
- package/skills/evaluate-behavior/scripts/lib/trials.mjs +524 -0
- package/skills/evaluate-behavior/scripts/lib/validity.mjs +68 -0
- package/skills/evaluate-behavior/scripts/lib/view-html.mjs +454 -0
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/view.mjs +159 -18
- package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-command-tool.mjs +21 -6
- package/skills/evaluate-behavior/scripts/pi-container-route.mjs +23 -0
- package/skills/evaluate-behavior/scripts/pi-guard.mjs +77 -0
- package/skills/evaluate-behavior/scripts/pi-login-refresh.mjs +31 -0
- package/skills/evaluate-behavior/scripts/skill-eval.mjs +586 -0
- package/skills/execute-work/SKILL.md +96 -104
- package/skills/execute-work/references/code-practices.md +27 -51
- package/skills/execute-work/references/design.md +271 -0
- package/skills/execute-work/references/diagnose.md +117 -0
- package/skills/execute-work/references/migration.md +108 -0
- package/skills/execute-work/references/simplify.md +65 -0
- package/skills/execute-work/scripts/self-review.mjs +422 -0
- package/skills/execute-work/scripts/touch-map.mjs +290 -0
- package/skills/review-work/SKILL.md +89 -65
- package/skills/review-work/references/adjudication.md +94 -0
- package/skills/review-work/references/artifact.md +198 -0
- package/skills/review-work/references/evals-and-judges.md +66 -0
- package/skills/review-work/references/work.md +177 -0
- package/skills/review-work/scripts/check-brief.mjs +128 -0
- package/skills/review-work/scripts/review-packet.mjs +233 -0
- package/skills/setup-freeflow/SKILL.md +68 -17
- package/skills/track-work/SKILL.md +52 -20
- package/skills/track-work/references/handoff.md +190 -0
- package/skills/track-work/references/projects.md +51 -0
- package/skills/track-work/references/working-record-format.md +36 -18
- package/skills/track-work/scripts/lib/document.mjs +17 -13
- package/skills/track-work/scripts/lib/format.mjs +2 -0
- package/skills/track-work/scripts/lib/recover.mjs +254 -0
- package/skills/track-work/scripts/lib/registry.mjs +656 -0
- package/skills/track-work/scripts/lib/search.mjs +40 -0
- package/skills/track-work/scripts/lib/store.mjs +46 -15
- package/skills/track-work/scripts/lib/views.mjs +50 -1
- package/skills/track-work/scripts/working-record.mjs +232 -13
- package/skills/verify-work/SKILL.md +45 -37
- package/skills/verify-work/references/feature-map.md +120 -0
- package/skills/verify-work/references/runtime-evidence.md +203 -0
- package/skills/verify-work/references/test-design.md +25 -6
- package/skills/verify-work/scripts/test-changes.mjs +246 -0
- package/skills/write-docs/SKILL.md +113 -25
- package/skills/write-docs/references/documentation-shapes.md +68 -3
- package/skills/write-docs/references/spec-and-plan-shapes.md +308 -0
- package/skills/write-docs/scripts/artifact-check.mjs +148 -0
- package/skills/write-docs/scripts/docs-check.mjs +96 -6
- package/skills/write-docs/scripts/plan-lint.mjs +123 -0
- package/skills/write-skill/SKILL.md +92 -52
- package/skills/write-skill/references/activation-and-wording.md +143 -0
- package/skills/write-skill/references/placement-and-dependencies.md +154 -0
- package/skills/write-skill/scripts/lib/compare.mjs +701 -0
- package/skills/write-skill/scripts/lib/skill-author.mjs +197 -3
- package/skills/write-skill/scripts/skill-author.mjs +51 -8
- package/capabilities/cognitive-routing/references/both-mode.md +0 -36
- package/capabilities/cognitive-routing/references/executor-mode.md +0 -48
- package/capabilities/cognitive-routing/references/helper-mode.md +0 -47
- package/plugin.json +0 -33
- package/skills/action-selection/SKILL.md +0 -92
- package/skills/action-selection/references/trajectory-stalls.md +0 -73
- package/skills/bypass/SKILL.md +0 -153
- package/skills/commit-work/SKILL.md +0 -93
- package/skills/commit-work/references/git-state-edges.md +0 -94
- package/skills/commit-work/references/simple-push.md +0 -94
- package/skills/commit-work/references/staging-decisions.md +0 -106
- package/skills/decision-gate/SKILL.md +0 -87
- package/skills/design-for-depth/SKILL.md +0 -108
- package/skills/design-for-depth/references/design-pressure-signals.md +0 -84
- package/skills/design-for-depth/references/implementation-decisions.md +0 -76
- package/skills/design-for-depth/references/interface-design-loop.md +0 -105
- package/skills/design-for-depth/references/module-and-dependency-design.md +0 -117
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -125
- package/skills/diagnose-failure/SKILL.md +0 -140
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -50
- package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
- package/skills/discuss/SKILL.md +0 -140
- package/skills/evaluate-skill/SKILL.md +0 -188
- package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -415
- package/skills/evaluate-skill/references/evaluation-design.md +0 -112
- package/skills/evaluate-skill/references/execution-and-evidence.md +0 -111
- package/skills/evaluate-skill/references/review-and-revision.md +0 -94
- package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -448
- package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +0 -60
- package/skills/evaluate-skill/scripts/lib/grade.mjs +0 -64
- package/skills/evaluate-skill/scripts/lib/review.mjs +0 -196
- package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -405
- package/skills/evaluate-skill/scripts/lib/trials.mjs +0 -103
- package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -181
- package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -182
- package/skills/execute-work/references/execute-work-edges.md +0 -78
- package/skills/finish-branch/SKILL.md +0 -117
- package/skills/finish-branch/references/integration-options.md +0 -80
- package/skills/handoff/SKILL.md +0 -82
- package/skills/handoff/references/handoff-templates.md +0 -150
- package/skills/launch-work/SKILL.md +0 -116
- package/skills/launch-work/references/launch-readiness.md +0 -85
- package/skills/migration-work/SKILL.md +0 -115
- package/skills/migration-work/references/migration-lifecycle.md +0 -82
- package/skills/release-work/SKILL.md +0 -118
- package/skills/release-work/references/release-evidence.md +0 -78
- package/skills/review-artifact/SKILL.md +0 -143
- package/skills/review-artifact/references/adjudicate-artifact-review.md +0 -101
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +0 -159
- package/skills/review-work/references/adjudicate-work-review.md +0 -97
- package/skills/review-work/references/independent-work-reviewer-contract.md +0 -139
- package/skills/review-work/references/security-risk-lens.md +0 -67
- package/skills/setup-freeflow/references/activation-contract.md +0 -103
- package/skills/setup-freeflow/references/host-setup.md +0 -67
- package/skills/simplify-code/SKILL.md +0 -85
- package/skills/simplify-code/references/simplification-patterns.md +0 -70
- package/skills/verify-work/references/browser-runtime-evidence.md +0 -51
- package/skills/verify-work/references/integration-evidence.md +0 -49
- package/skills/verify-work/references/performance-evidence.md +0 -59
- package/skills/workflow/SKILL.md +0 -171
- package/skills/workflow/references/domain-skill-composition.md +0 -45
- package/skills/write-plan/SKILL.md +0 -92
- package/skills/write-plan/references/plan-shapes.md +0 -156
- package/skills/write-skill/references/activation-boundaries.md +0 -106
- package/skills/write-skill/references/agent-first-instructions.md +0 -133
- package/skills/write-skill/references/progressive-disclosure.md +0 -129
- package/skills/write-skill/references/skill-dependency-graphs.md +0 -192
- package/skills/write-spec/SKILL.md +0 -85
- package/skills/write-spec/references/artifact-standards.md +0 -86
- package/skills/write-spec/references/decision-records.md +0 -80
- package/skills/write-spec/references/spec-shapes.md +0 -143
- /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
- /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +0 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// Tracked metrics: what each run cost and how it went, derived only from evidence the run already records.
|
|
2
|
+
// Importing this module registers the run-metrics hook and the `metrics` aggregator; the CLI imports it once.
|
|
3
|
+
// A metric whose evidence is missing is null, never 0, so an absent measurement cannot read as a free one.
|
|
4
|
+
|
|
5
|
+
import { realpathSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
import { measuredCost } from "./budget.mjs";
|
|
9
|
+
import { METRIC_NAMES } from "./decision.mjs";
|
|
10
|
+
import { registerAggregator, registerRunMetricsHook } from "./hooks.mjs";
|
|
11
|
+
|
|
12
|
+
// The names are kept with the decision rule, which checks them when a suite loads, so loading a definition
|
|
13
|
+
// registers no metrics hook.
|
|
14
|
+
export { METRIC_NAMES };
|
|
15
|
+
|
|
16
|
+
// No tokenizer ships with the evaluator, so skill-read tokens are estimated from the bytes that entered the context.
|
|
17
|
+
export const BYTES_PER_TOKEN = 4;
|
|
18
|
+
// The directories of a Freeflow package that hold model-facing instructions: skills, capability skills and prompts.
|
|
19
|
+
const FREEFLOW_INSTRUCTION_DIRECTORIES = ["skills", "capabilities", "runtime"];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* One run's metrics. RPC observations record turns, each with its tool activity; a one-shot observation records
|
|
23
|
+
* one turn's tool activity at top level. Usage is the run's merged usage.
|
|
24
|
+
*
|
|
25
|
+
* `peakContext` and the skill reads come from the run's transcript (see `peakContext` and `skillReads`), so they are
|
|
26
|
+
* measured on Pi only; on another host, or without a transcript, they are null.
|
|
27
|
+
*
|
|
28
|
+
* @param {{ observation?: any, run: any, subject?: any }} context
|
|
29
|
+
*/
|
|
30
|
+
export function runMetricsFor({ observation, run, subject }) {
|
|
31
|
+
const turns = Array.isArray(observation?.turns) ? observation.turns : Array.isArray(run?.turns) ? run.turns : null;
|
|
32
|
+
const activity = turns !== null ? turnActivity(turns) : topLevelActivity(observation, run);
|
|
33
|
+
const usage = isObject(run?.usage) ? run.usage : isObject(observation?.usage) ? observation.usage : null;
|
|
34
|
+
const tokens = {
|
|
35
|
+
input: count(usage?.input),
|
|
36
|
+
output: count(usage?.output),
|
|
37
|
+
cacheRead: count(usage?.cacheRead),
|
|
38
|
+
cacheWrite: count(usage?.cacheWrite),
|
|
39
|
+
};
|
|
40
|
+
const prompted =
|
|
41
|
+
tokens.input === null || tokens.cacheRead === null || tokens.cacheWrite === null
|
|
42
|
+
? null
|
|
43
|
+
: tokens.input + tokens.cacheRead + tokens.cacheWrite;
|
|
44
|
+
return {
|
|
45
|
+
durationMs: duration(run?.startedAt, run?.completedAt),
|
|
46
|
+
turns: turns === null ? 1 : turns.length,
|
|
47
|
+
toolCalls: activity === null ? null : activity.length,
|
|
48
|
+
toolErrors: activity === null ? null : activity.filter((entry) => entry?.isError === true).length,
|
|
49
|
+
tokens,
|
|
50
|
+
// Null when unmeasured: a zero cost for a run that used tokens is a missing price, not a free run.
|
|
51
|
+
cost: measuredCost(usage),
|
|
52
|
+
// The share of prompt tokens served from the provider's cache.
|
|
53
|
+
cacheHitRate: prompted === null || prompted === 0 ? null : tokens.cacheRead / prompted,
|
|
54
|
+
...transcriptMetrics({ run, subject }),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The transcript-derived metrics, null unless the run is a Pi run with a transcript. */
|
|
59
|
+
function transcriptMetrics({ run, subject }) {
|
|
60
|
+
const host = subject?.host?.name ?? run?.process?.host ?? null;
|
|
61
|
+
const transcript = Array.isArray(run?.transcript) ? run.transcript : null;
|
|
62
|
+
if (host !== "pi" || transcript === null) return { peakContext: null, skillReads: null, skillReadTokens: null };
|
|
63
|
+
const reads = skillReads(transcript, { roots: skillRoots(subject), cwd: run?.workspace ?? subject?.workspace });
|
|
64
|
+
return { peakContext: peakContext(transcript), skillReads: reads.reads, skillReadTokens: reads.tokens };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The largest single request a run sent: for each assistant message (one model request) of a Pi transcript, its
|
|
69
|
+
* input tokens plus cache reads plus cache writes, which together are the whole prompt that request carried. Null when
|
|
70
|
+
* no assistant message records all three.
|
|
71
|
+
*
|
|
72
|
+
* @param {any[]} transcript
|
|
73
|
+
*/
|
|
74
|
+
export function peakContext(transcript) {
|
|
75
|
+
let peak = null;
|
|
76
|
+
for (const message of transcript) {
|
|
77
|
+
if (message?.role !== "assistant" || !isObject(message.usage)) continue;
|
|
78
|
+
const parts = [message.usage.input, message.usage.cacheRead, message.usage.cacheWrite].map(count);
|
|
79
|
+
if (parts.includes(null)) continue;
|
|
80
|
+
const size = parts[0] + parts[1] + parts[2];
|
|
81
|
+
if (peak === null || size > peak) peak = size;
|
|
82
|
+
}
|
|
83
|
+
return peak;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Directories whose files count as skill reads for a subject: each declared skill, and the instruction directories
|
|
88
|
+
* (`skills`, `capabilities`, `runtime`) of the Freeflow package the arm loads. Each is listed as given and by its
|
|
89
|
+
* real path, since a path in the transcript may use either.
|
|
90
|
+
*/
|
|
91
|
+
export function skillRoots(subject) {
|
|
92
|
+
const roots = [
|
|
93
|
+
...(subject?.environment?.skills ?? []).map((skill) => skill?.path),
|
|
94
|
+
...(typeof subject?.freeflowRoot === "string"
|
|
95
|
+
? FREEFLOW_INSTRUCTION_DIRECTORIES.map((directory) => path.join(subject.freeflowRoot, directory))
|
|
96
|
+
: []),
|
|
97
|
+
].filter((root) => typeof root === "string" && path.isAbsolute(root));
|
|
98
|
+
return [...new Set(roots.flatMap((root) => [path.resolve(root), realOr(root)]))];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Every successful read of a skill file in a Pi transcript, re-reads included: a `read` call whose path lies inside
|
|
103
|
+
* one of `roots`, and a `bash` call whose command names a path inside one, such as `cat <skill>/SKILL.md`. Each
|
|
104
|
+
* counts the text its result put into the context, so a partial read counts what it returned and a bash command
|
|
105
|
+
* counts its whole output. Tokens are estimated as UTF-8 bytes over `BYTES_PER_TOKEN`, rounded up per read.
|
|
106
|
+
* Returns `{ reads, tokens, bytes }`; zero when the arm has no skills or read none.
|
|
107
|
+
*
|
|
108
|
+
* @param {any[]} transcript
|
|
109
|
+
* @param {{ roots: string[], cwd?: string }} options
|
|
110
|
+
*/
|
|
111
|
+
export function skillReads(transcript, { roots, cwd }) {
|
|
112
|
+
const calls = new Map();
|
|
113
|
+
let reads = 0;
|
|
114
|
+
let tokens = 0;
|
|
115
|
+
let bytes = 0;
|
|
116
|
+
if (roots.length === 0) return { reads, tokens, bytes };
|
|
117
|
+
for (const message of transcript) {
|
|
118
|
+
if (message?.role === "assistant" && Array.isArray(message.content)) {
|
|
119
|
+
for (const block of message.content) {
|
|
120
|
+
if (block?.type === "toolCall" && typeof block.id === "string") calls.set(block.id, block);
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (message?.role !== "toolResult" || message.isError === true) continue;
|
|
125
|
+
const call = calls.get(message.toolCallId);
|
|
126
|
+
if (call === undefined || !readsSkill(call, roots, cwd)) continue;
|
|
127
|
+
const size = Buffer.byteLength(resultText(message), "utf8");
|
|
128
|
+
reads += 1;
|
|
129
|
+
bytes += size;
|
|
130
|
+
tokens += Math.ceil(size / BYTES_PER_TOKEN);
|
|
131
|
+
}
|
|
132
|
+
return { reads, tokens, bytes };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function readsSkill(call, roots, cwd) {
|
|
136
|
+
const args = isObject(call.arguments) ? call.arguments : {};
|
|
137
|
+
if (call.name === "read" && typeof args.path === "string") {
|
|
138
|
+
const target = resolveReadPath(args.path, cwd);
|
|
139
|
+
return roots.some((root) => target === root || target.startsWith(`${root}${path.sep}`));
|
|
140
|
+
}
|
|
141
|
+
if (call.name === "bash" && typeof args.command === "string") {
|
|
142
|
+
return roots.some((root) => new RegExp(`${escapeRegExp(root)}(?:/|$|["'\\s])`).test(args.command));
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function escapeRegExp(text) {
|
|
148
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function resolveReadPath(value, cwd) {
|
|
152
|
+
const written = value.startsWith("@") ? value.slice(1) : value;
|
|
153
|
+
return path.resolve(typeof cwd === "string" ? cwd : "/", written);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function resultText(message) {
|
|
157
|
+
if (typeof message.content === "string") return message.content;
|
|
158
|
+
if (!Array.isArray(message.content)) return "";
|
|
159
|
+
return message.content
|
|
160
|
+
.filter((part) => part?.type === "text" && typeof part.text === "string")
|
|
161
|
+
.map((part) => part.text)
|
|
162
|
+
.join("");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function realOr(target) {
|
|
166
|
+
try {
|
|
167
|
+
return realpathSync(target);
|
|
168
|
+
} catch {
|
|
169
|
+
return path.resolve(target);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** A run's metrics as flat `{ name: number | null }` values; a run without recorded metrics has every value null. */
|
|
174
|
+
export function metricValues(run) {
|
|
175
|
+
const metrics = isObject(run?.metrics) ? run.metrics : {};
|
|
176
|
+
return Object.fromEntries(
|
|
177
|
+
METRIC_NAMES.map((name) => [name, number(name.split(".").reduce((value, key) => value?.[key], metrics))]),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The `metrics` aggregate section for one group: per arm, each metric's count, median, mean, min and max over the
|
|
183
|
+
* trials that measured it; per non-reference arm, the paired difference (arm minus reference, within each trial
|
|
184
|
+
* where both were measured); and every trial's values per the shared measures contract.
|
|
185
|
+
*
|
|
186
|
+
* @param {{ arms: string[], reference: string, trials: Array<{ id: string | null, runs: Record<string, any> }> }} input
|
|
187
|
+
*/
|
|
188
|
+
export function aggregateMetrics({ arms, reference, trials }) {
|
|
189
|
+
const perTrial = [];
|
|
190
|
+
for (const trial of trials) {
|
|
191
|
+
// Only a complete run's metrics count: an invalid, failed or cancelled run's cost or turns are no measure of
|
|
192
|
+
// the arm, and must never reach a decision; nor do a run's held for transcript flags no one has cleared.
|
|
193
|
+
for (const arm of arms) {
|
|
194
|
+
const run = trial.runs?.[arm];
|
|
195
|
+
const counted = run?.state === "complete" && !trial.held?.includes(arm);
|
|
196
|
+
perTrial.push({ trial: trial.id, arm, values: metricValues(counted ? run : null) });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const valuesOf = (arm, name) => perTrial.filter((entry) => entry.arm === arm).map((entry) => entry.values[name]);
|
|
200
|
+
const summaries = {};
|
|
201
|
+
for (const arm of arms) {
|
|
202
|
+
summaries[arm] = Object.fromEntries(METRIC_NAMES.map((name) => [name, summarize(valuesOf(arm, name))]));
|
|
203
|
+
}
|
|
204
|
+
const differences = {};
|
|
205
|
+
for (const arm of arms) {
|
|
206
|
+
if (arm === reference || !arms.includes(reference)) continue;
|
|
207
|
+
differences[arm] = Object.fromEntries(
|
|
208
|
+
METRIC_NAMES.map((name) => {
|
|
209
|
+
const referenceValues = valuesOf(reference, name);
|
|
210
|
+
const paired = valuesOf(arm, name)
|
|
211
|
+
.map((value, index) =>
|
|
212
|
+
value === null || referenceValues[index] === null ? null : value - referenceValues[index],
|
|
213
|
+
)
|
|
214
|
+
.filter((value) => value !== null);
|
|
215
|
+
const { count: pairs, mean, median } = summarize(paired);
|
|
216
|
+
return [name, { pairs, mean, median }];
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
return { reference, arms: summaries, differences, perTrial };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** View rows for one run's metrics; tokens are already on the usage row. */
|
|
224
|
+
export function runMetricsLines(run) {
|
|
225
|
+
if (!isObject(run?.metrics)) return [];
|
|
226
|
+
const values = metricValues(run);
|
|
227
|
+
return [
|
|
228
|
+
row(" metrics", [
|
|
229
|
+
`duration-ms=${format(values.durationMs)}`,
|
|
230
|
+
`turns=${format(values.turns)}`,
|
|
231
|
+
`tool-calls=${format(values.toolCalls)}`,
|
|
232
|
+
`tool-errors=${format(values.toolErrors)}`,
|
|
233
|
+
`cache-hit-rate=${format(values.cacheHitRate)}`,
|
|
234
|
+
`cost=${format(values.cost)}`,
|
|
235
|
+
]),
|
|
236
|
+
...(values.peakContext === null && values.skillReads === null
|
|
237
|
+
? []
|
|
238
|
+
: [
|
|
239
|
+
row(" context", [
|
|
240
|
+
`peak-context=${format(values.peakContext)}`,
|
|
241
|
+
`skill-reads=${format(values.skillReads)}`,
|
|
242
|
+
`skill-read-tokens~=${format(values.skillReadTokens)}`,
|
|
243
|
+
]),
|
|
244
|
+
]),
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** View rows for an aggregate group's metrics section: one row per metric any arm measured. */
|
|
249
|
+
export function aggregateMetricsLines(section) {
|
|
250
|
+
if (!isObject(section)) return [];
|
|
251
|
+
if (typeof section.error === "string") return [row("metrics", ["error", section.error])];
|
|
252
|
+
const lines = [];
|
|
253
|
+
for (const name of METRIC_NAMES) {
|
|
254
|
+
const arms = Object.entries(section.arms ?? {});
|
|
255
|
+
if (arms.every(([, metrics]) => !metrics?.[name]?.count)) continue;
|
|
256
|
+
lines.push(
|
|
257
|
+
row("metric", [
|
|
258
|
+
name,
|
|
259
|
+
...arms.map(([arm, metrics]) => {
|
|
260
|
+
const summary = metrics?.[name];
|
|
261
|
+
return `${arm}=median:${format(summary?.median)} mean:${format(summary?.mean)} n=${summary?.count ?? 0}`;
|
|
262
|
+
}),
|
|
263
|
+
...Object.entries(section.differences ?? {}).map(([arm, metrics]) => {
|
|
264
|
+
const difference = metrics?.[name];
|
|
265
|
+
return `${arm}-${section.reference}=mean:${format(difference?.mean, true)} pairs=${difference?.pairs ?? 0}`;
|
|
266
|
+
}),
|
|
267
|
+
]),
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return lines;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function summarize(values) {
|
|
274
|
+
const measured = values.filter((value) => value !== null).sort((left, right) => left - right);
|
|
275
|
+
const n = measured.length;
|
|
276
|
+
if (n === 0) return { count: 0, median: null, mean: null, min: null, max: null };
|
|
277
|
+
const middle = Math.floor(n / 2);
|
|
278
|
+
return {
|
|
279
|
+
count: n,
|
|
280
|
+
median: n % 2 === 1 ? measured[middle] : (measured[middle - 1] + measured[middle]) / 2,
|
|
281
|
+
mean: measured.reduce((total, value) => total + value, 0) / n,
|
|
282
|
+
min: measured[0],
|
|
283
|
+
max: measured[n - 1],
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function turnActivity(turns) {
|
|
288
|
+
if (!turns.every((turn) => Array.isArray(turn?.toolActivity))) return null;
|
|
289
|
+
return turns.flatMap((turn) => turn.toolActivity);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function topLevelActivity(observation, run) {
|
|
293
|
+
if (Array.isArray(observation?.toolActivity)) return observation.toolActivity;
|
|
294
|
+
if (Array.isArray(run?.toolActivity)) return run.toolActivity;
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function duration(startedAt, completedAt) {
|
|
299
|
+
const start = typeof startedAt === "string" ? Date.parse(startedAt) : Number.NaN;
|
|
300
|
+
const end = typeof completedAt === "string" ? Date.parse(completedAt) : Number.NaN;
|
|
301
|
+
return Number.isFinite(start) && Number.isFinite(end) ? end - start : null;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function count(value) {
|
|
305
|
+
return Number.isInteger(value) && value >= 0 ? value : null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function number(value) {
|
|
309
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function isObject(value) {
|
|
313
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function format(value, signed = false) {
|
|
317
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return "unavailable";
|
|
318
|
+
const text = Number.isInteger(value) ? String(value) : String(Number(value.toFixed(6)));
|
|
319
|
+
return signed && value > 0 ? `+${text}` : text;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Same cell escaping as view.mjs, so a metrics row reads like every other view row.
|
|
323
|
+
function row(kind, values) {
|
|
324
|
+
return [kind, ...values]
|
|
325
|
+
.map((value) =>
|
|
326
|
+
String(value).replace(/\\/g, "\\\\").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t"),
|
|
327
|
+
)
|
|
328
|
+
.join("\t");
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
registerRunMetricsHook("tracked", runMetricsFor);
|
|
332
|
+
registerAggregator("metrics", aggregateMetrics);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// One model call that is not a subject: a judge, a reviewer, or a simulated user. It runs on a host (Pi unless the
|
|
2
|
+
// setup names another) without tools, skills, context files or session, in its own isolated directories; the host
|
|
3
|
+
// adapter in hosts/ owns how.
|
|
4
|
+
|
|
5
|
+
import { hostAdapter } from "./hosts/index.mjs";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Starts one model call on a prompt in its own directory, which keeps the prompt, events, final response and stderr.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} directory replaced on every call
|
|
11
|
+
* @param {string} prompt
|
|
12
|
+
* @param {{host?: string, model: string, thinking: string | null, extensions: Array<{path: string}>, settings: object, signal?: AbortSignal, role?: string}} options
|
|
13
|
+
* @returns {Promise<{response: string | null, errors: string[], usage: any, isolation: any}>}
|
|
14
|
+
*/
|
|
15
|
+
export async function launchModel(directory, prompt, options) {
|
|
16
|
+
return hostAdapter(options.host ?? "pi").modelCall(directory, prompt, options);
|
|
17
|
+
}
|