oh-my-knowledge 1.0.0-beta.12 → 1.0.0-beta.13
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/README.md +15 -3
- package/README.zh.md +15 -3
- package/dist/assets/agent-skills/omk/references/commands.md +20 -0
- package/dist/cli/commands/eval/task.d.ts +13 -0
- package/dist/cli/commands/eval/task.js +42 -0
- package/dist/eval-workflows/hosts/local-task/acceptance.d.ts +13 -0
- package/dist/eval-workflows/hosts/local-task/acceptance.js +101 -0
- package/dist/eval-workflows/hosts/local-task/application.d.ts +99 -0
- package/dist/eval-workflows/hosts/local-task/application.js +177 -0
- package/dist/eval-workflows/hosts/local-task/diagnostics.d.ts +27 -0
- package/dist/eval-workflows/hosts/local-task/diagnostics.js +30 -0
- package/dist/eval-workflows/hosts/local-task/executor.d.ts +19 -0
- package/dist/eval-workflows/hosts/local-task/executor.js +135 -0
- package/dist/eval-workflows/hosts/local-task/files.d.ts +15 -0
- package/dist/eval-workflows/hosts/local-task/files.js +88 -0
- package/dist/eval-workflows/hosts/local-task/workspace.d.ts +6 -0
- package/dist/eval-workflows/hosts/local-task/workspace.js +32 -0
- package/dist/eval-workflows/inputs/contracts/local-task-evidence.d.ts +46 -0
- package/dist/eval-workflows/inputs/contracts/local-task-evidence.js +26 -0
- package/dist/eval-workflows/inputs/contracts/local-task.d.ts +50 -0
- package/dist/eval-workflows/inputs/contracts/local-task.js +33 -0
- package/dist/eval-workflows/projections/local-task.d.ts +23 -0
- package/dist/eval-workflows/projections/local-task.js +25 -0
- package/dist/executors/openai/codex/protocol.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/knowledge/contracts.d.ts +4 -4
- package/dist/knowledge/store.d.ts +3 -3
- package/dist/observability/contracts/experience-evidence-schema.d.ts +29 -29
- package/dist/observability/knowledge-extraction/application.d.ts +10 -10
- package/dist/observability/knowledge-extraction/proposals.d.ts +2 -2
- package/dist/observability/knowledge-extraction/runs.d.ts +2 -2
- package/dist/studio/application/measure/core-run-format.js +2 -1
- package/dist/studio/application/measure/core-run-projection.js +12 -0
- package/dist/studio/http/next-server.js +1 -1
- package/dist/studio/http/pages/measure-page.d.ts +1 -0
- package/dist/studio/http/pages/measure-page.js +2 -2
- package/dist/studio/view-models/measure/core-runs.d.ts +32 -0
- package/dist/studio/web/.next/BUILD_ID +1 -1
- package/dist/studio/web/.next/app-path-routes-manifest.json +3 -3
- package/dist/studio/web/.next/build-manifest.json +4 -4
- package/dist/studio/web/.next/prerender-manifest.json +3 -3
- package/dist/studio/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/_global-error.html +1 -1
- package/dist/studio/web/.next/server/app/_global-error.rsc +4 -4
- package/dist/studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +4 -4
- package/dist/studio/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +4 -4
- package/dist/studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/studio/web/.next/server/app/_not-found/page.js +1 -1
- package/dist/studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/agents/page.js +1 -1
- package/dist/studio/web/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/candidates/page.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/candidates/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/managed/[managedId]/page.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/managed/[managedId]/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/knowledge/managed/[managedId]/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/managed/page.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/managed/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/knowledge/managed/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/page.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/knowledge/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/skills/[skillName]/page.js +1 -1
- package/dist/studio/web/.next/server/app/knowledge/skills/[skillName]/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/knowledge/skills/[skillName]/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/measure/[runId]/page.js +1 -1
- package/dist/studio/web/.next/server/app/measure/[runId]/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/measure/[runId]/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/measure/page.js +1 -1
- package/dist/studio/web/.next/server/app/measure/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/measure/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/observe/conversations/[threadId]/page.js +1 -1
- package/dist/studio/web/.next/server/app/observe/conversations/[threadId]/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/observe/conversations/[threadId]/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/observe/conversations/[threadId]/tasks/[turnId]/page.js +1 -1
- package/dist/studio/web/.next/server/app/observe/conversations/[threadId]/tasks/[turnId]/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/observe/conversations/[threadId]/tasks/[turnId]/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/observe/health/[analysisId]/page.js +1 -1
- package/dist/studio/web/.next/server/app/observe/health/[analysisId]/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/observe/health/[analysisId]/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/observe/health/page.js +1 -1
- package/dist/studio/web/.next/server/app/observe/health/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/observe/health/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/observe/health-diff/page.js +1 -1
- package/dist/studio/web/.next/server/app/observe/health-diff/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/observe/health-diff/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/observe/inbox/page.js +2 -2
- package/dist/studio/web/.next/server/app/observe/inbox/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/observe/inbox/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/observe/page.js +1 -1
- package/dist/studio/web/.next/server/app/observe/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/observe/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app/observe/skill-trend/[skillName]/page.js +1 -1
- package/dist/studio/web/.next/server/app/observe/skill-trend/[skillName]/page.js.nft.json +1 -1
- package/dist/studio/web/.next/server/app/observe/skill-trend/[skillName]/page_client-reference-manifest.js +1 -1
- package/dist/studio/web/.next/server/app-paths-manifest.json +3 -3
- package/dist/studio/web/.next/server/chunks/3.js +1 -2
- package/dist/studio/web/.next/server/chunks/335.js +1 -1
- package/dist/studio/web/.next/server/chunks/374.js +1 -1
- package/dist/studio/web/.next/server/chunks/475.js +1 -1
- package/dist/studio/web/.next/server/chunks/499.js +1 -1
- package/dist/studio/web/.next/server/chunks/5.js +1 -0
- package/dist/studio/web/.next/server/chunks/740.js +3 -0
- package/dist/studio/web/.next/server/chunks/789.js +6 -0
- package/dist/studio/web/.next/server/chunks/827.js +4 -4
- package/dist/studio/web/.next/server/chunks/84.js +1 -1
- package/dist/studio/web/.next/server/chunks/955.js +1 -1
- package/dist/studio/web/.next/server/chunks/996.js +1 -0
- package/dist/studio/web/.next/server/middleware-build-manifest.js +1 -1
- package/dist/studio/web/.next/server/pages/500.html +1 -1
- package/dist/studio/web/.next/server/server-reference-manifest.json +1 -1
- package/dist/studio/web/.next/server/webpack-runtime.js +1 -1
- package/dist/studio/web/.next/static/chunks/{1858-c24dcd678cdcae50.js → 1858-3ad7971aef7540de.js} +1 -1
- package/dist/studio/web/.next/static/chunks/2038-b941bab2ee1ef91a.js +1 -0
- package/dist/studio/web/.next/static/chunks/{2666-2770e8e4b4caab0e.js → 2666-022e0893a8ce80b1.js} +1 -1
- package/dist/studio/web/.next/static/chunks/{285-6d4538bae9d2657f.js → 285-ee2bde496eae900b.js} +1 -1
- package/dist/studio/web/.next/static/chunks/{2953-a2797c8e2c97e052.js → 2953-32389c228338c03f.js} +1 -1
- package/dist/studio/web/.next/static/chunks/3958-b95d9b60fc7f9eaf.js +1 -0
- package/dist/studio/web/.next/static/chunks/{4684-f60cec1af16cc02a.js → 4684-62296d31718b47b2.js} +1 -1
- package/dist/studio/web/.next/static/chunks/5067-75d71f6808309f44.js +6 -0
- package/dist/studio/web/.next/static/chunks/5669-9f0ec3bc92ab3392.js +3 -0
- package/dist/studio/web/.next/static/chunks/{5834-4a1f3c2c259a1918.js → 5834-ceadd3ef88dd5027.js} +1 -1
- package/dist/studio/web/.next/static/chunks/{6651-3212253f7d2d141f.js → 6651-392fd48cf473d253.js} +2 -2
- package/dist/studio/web/.next/static/chunks/{7576-c700fccfc712c394.js → 7576-0651db5aa8473f62.js} +2 -2
- package/dist/studio/web/.next/static/chunks/8284-399476b82eb468d0.js +1 -0
- package/dist/studio/web/.next/static/chunks/{8791-628c2e7b0dab299a.js → 8791-cc9640eb97b0ac98.js} +1 -1
- package/dist/studio/web/.next/static/chunks/9688-8fc2ea8f15ab0642.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/agents/page-5566ed76365fc033.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/{error-66775c241a4b5db1.js → error-da3a9e3657f1dc15.js} +1 -1
- package/dist/studio/web/.next/static/chunks/app/knowledge/candidates/{page-4202293fa248f263.js → page-8f6377569b01b64e.js} +1 -1
- package/dist/studio/web/.next/static/chunks/app/knowledge/managed/[managedId]/page-e8aa43310b5ab528.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/knowledge/managed/page-cbae9028d8b57dcf.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/knowledge/page-92a6b1ddc3e44d8b.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/knowledge/skills/[skillName]/page-082ada30d2554c7e.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/layout-872f1dfcd7ede526.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/{loading-cc65be1028090c98.js → loading-459503db9bf76ee0.js} +1 -1
- package/dist/studio/web/.next/static/chunks/app/measure/[runId]/page-1b6e584362fa1fc7.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/measure/page-94c52f9a44a8b88e.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/{not-found-bcac3c26cbb1b3ed.js → not-found-5b86ee2b9fd19a95.js} +1 -1
- package/dist/studio/web/.next/static/chunks/app/observe/conversations/[threadId]/page-327796657a0b2692.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/observe/conversations/[threadId]/tasks/[turnId]/page-1c806e81831e69b9.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/observe/health/[analysisId]/page-41dd612e29247f6f.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/observe/health/page-66161c7025058683.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/observe/health-diff/page-373e7ca6f18a4594.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/observe/inbox/{page-5003e5561490047b.js → page-923860677b6af0ad.js} +1 -1
- package/dist/studio/web/.next/static/chunks/app/observe/page-ff04bfbae9e9c409.js +1 -0
- package/dist/studio/web/.next/static/chunks/app/observe/skill-trend/[skillName]/page-4711e930a7c6a790.js +1 -0
- package/dist/studio/web/.next/static/chunks/{main-c9db6abe2bb89249.js → main-be4276342bf776ea.js} +1 -1
- package/dist/studio/web/.next/static/chunks/webpack-ac24233801701b11.js +1 -0
- package/dist/studio/web/.next/static/css/bc117f6d956d35b6.css +1 -0
- package/dist/studio/web/.next/trace +47 -42
- package/dist/studio/web/.next/trace-build +1 -1
- package/package.json +5 -3
- package/dist/studio/web/.next/server/chunks/209.js +0 -1
- package/dist/studio/web/.next/server/chunks/384.js +0 -1
- package/dist/studio/web/.next/server/chunks/388.js +0 -1
- package/dist/studio/web/.next/server/chunks/42.js +0 -1
- package/dist/studio/web/.next/server/chunks/767.js +0 -6
- package/dist/studio/web/.next/static/chunks/2038-fe4b8b7ee5f7cd19.js +0 -1
- package/dist/studio/web/.next/static/chunks/6250-1c80598e5a04a823.js +0 -2
- package/dist/studio/web/.next/static/chunks/8038-9d97f8fe79c7f7d4.js +0 -6
- package/dist/studio/web/.next/static/chunks/8284-dca5d2249647425f.js +0 -1
- package/dist/studio/web/.next/static/chunks/9386-d62507b1afffde3c.js +0 -1
- package/dist/studio/web/.next/static/chunks/9688-568cb3f8978b37e8.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/agents/page-3cd33e679bd540fb.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/knowledge/managed/[managedId]/page-9f18fce00bf621dc.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/knowledge/managed/page-4e9b40acd4e5130b.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/knowledge/page-673896d1f93ec57d.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/knowledge/skills/[skillName]/page-28c63ecbe6a8d515.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/layout-26d16d7c66539fc1.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/measure/[runId]/page-f6b816f8eb408bb9.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/measure/page-8d42458a7c786a5a.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/observe/conversations/[threadId]/page-ebc25fa3c7ad85c3.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/observe/conversations/[threadId]/tasks/[turnId]/page-2602b9d82e415bfc.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/observe/health/[analysisId]/page-b32cc1278399d1a0.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/observe/health/page-3d8f7c136a55c349.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/observe/health-diff/page-3ca3d158cd147ef5.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/observe/page-4a9a6d1984a94eff.js +0 -1
- package/dist/studio/web/.next/static/chunks/app/observe/skill-trend/[skillName]/page-1a9d9d1ac593f16d.js +0 -1
- package/dist/studio/web/.next/static/chunks/webpack-040077c7c5b63775.js +0 -1
- package/dist/studio/web/.next/static/css/b1a43ef7dccd6013.css +0 -1
- /package/dist/studio/web/.next/static/{d2gceAJH4KOAP1AWstMW0 → Ahppnr7sbzLwhsw77RqCy}/_buildManifest.js +0 -0
- /package/dist/studio/web/.next/static/{d2gceAJH4KOAP1AWstMW0 → Ahppnr7sbzLwhsw77RqCy}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -12,12 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
OMK is a toolkit for observing, evaluating, and improving knowledge in AI applications. Inspect how real tasks ran, extract reviewable knowledge items from work logs, and measure changes to prompts, RAG, skills, agents, or workflows to inform adoption, rollback, and further improvement.
|
|
14
14
|
|
|
15
|
-
**Observe** preserves real behavior and its sources. **Measure**
|
|
15
|
+
**Observe** preserves real behavior and its sources. **Measure** fixes models and cases, controls and records execution conditions, and compares knowledge artifacts. **Know** connects decisions back to evidence. If you already have two artifact versions, start directly with evaluation; logs are not a prerequisite.
|
|
16
16
|
|
|
17
17
|
Version 1.0 is still **in Beta iteration**; APIs and storage contracts may change. Read the [migration guide](docs/guides/v1-preview-migration.md) before upgrading an older installation.
|
|
18
18
|
|
|
19
19
|

|
|
20
20
|
|
|
21
|
+
The diagram describes composable workflows: review knowledge and manually apply it to an artifact. Complete links across these stages and an automated end-to-end workflow remain partly unimplemented.
|
|
22
|
+
|
|
21
23
|
## Choose how to use OMK
|
|
22
24
|
|
|
23
25
|
| Interface | Use it for |
|
|
@@ -35,6 +37,7 @@ Version 1.0 is still **in Beta iteration**; APIs and storage contracts may chang
|
|
|
35
37
|
| Preserve knowledge from work | [Extract knowledge from logs](docs/guides/extract-knowledge.md) | Candidate knowledge with sources and conditions; revise, retain, or discard it |
|
|
36
38
|
| Record a confirmed knowledge problem | [MCP feedback](docs/guides/mcp-integration.md) | An observation for review; draft a case after confirming a real issue |
|
|
37
39
|
| Decide whether to adopt a change | [Evaluation quickstart](docs/quickstart-skill-eval.md) | A version decision, uncertainty, failed cases, and scoring evidence |
|
|
40
|
+
| Verify that an agent completed a task | [Local task acceptance](docs/guides/local-tasks.md) | Modified files, independent checks, and diagnostics after failure, timeout, or cancellation |
|
|
38
41
|
| Try improving a skill automatically | [Iterative improvement](docs/guides/auto-improve-skills.md) | Candidate versions screened through controlled comparisons |
|
|
39
42
|
| Embed evaluation in a service or platform | [Node.js service](docs/guides/eval-runtime.md) · [Platform host](docs/guides/platform-host-integration.md) | Composable execution, scoring, and comparison interfaces |
|
|
40
43
|
|
|
@@ -46,6 +49,8 @@ Installation requires Node.js >=22. Model calls also require an authenticated ru
|
|
|
46
49
|
npm i -g oh-my-knowledge@next
|
|
47
50
|
```
|
|
48
51
|
|
|
52
|
+
Local task acceptance requires `1.0.0-beta.13` or later. Check `omk --version` after installation, then follow the [local-task guide](docs/guides/local-tasks.md) to run a trusted example.
|
|
53
|
+
|
|
49
54
|
### Inspect an existing task
|
|
50
55
|
|
|
51
56
|
```bash
|
|
@@ -74,6 +79,12 @@ The scaffold contains two skills and three cases. This small default set checks
|
|
|
74
79
|
|
|
75
80
|
The [full walkthrough](docs/quickstart-skill-eval.md) covers runtime selection, your own skills, and interpreting results. The [example gallery](examples/README.md) offers more runnable scenarios.
|
|
76
81
|
|
|
82
|
+
### Verify the actual file changes
|
|
83
|
+
|
|
84
|
+
Local tasks run two knowledge variants from declared file snapshots, collect files, and check them with an independent verifier. Failures, timeouts, and cancellations also retain declared files and partial logs. This measures explicitly supplied knowledge content, not native skill discovery. Tasks and verifiers must be trusted; workspace copies are not security sandboxes.
|
|
85
|
+
|
|
86
|
+
The [cart example with an npm dependency](examples/local-task/project/README.md) records one real Codex run: treatment changed the target source and passed six checks; the negative control, instructed not to edit, passed three. This establishes a working execution and acceptance path, not general knowledge effectiveness. Model settings, usage, and durations are recorded in the example. The [local-task guide](docs/guides/local-tasks.md) covers installation, configuration, and evidence access.
|
|
87
|
+
|
|
77
88
|
## Use inside agents and DSH
|
|
78
89
|
|
|
79
90
|
### Agent Skill and MCP
|
|
@@ -116,9 +127,9 @@ A knowledge item is a reviewable fact, experience, or method. An artifact is a p
|
|
|
116
127
|
|
|
117
128
|
These paths compose; they are not a mandatory linear state machine. Passing an evaluation does not mean knowledge has been written or a version published.
|
|
118
129
|
|
|
119
|
-
Conclusions depend on the cases, scoring criteria, and execution environment. Observation signals are not causal conclusions, and generated cases are not an independent release-validation set. See [statistical rigor](docs/explanation/statistical-rigor.md) and the [three-stage workflow](docs/explanation/three-stage-workflow.md) for the method and limits.
|
|
130
|
+
Fixed configuration cannot guarantee identical external model services or rerun outcomes. Conclusions depend on the cases, scoring criteria, and execution environment. Observation signals are not causal conclusions, and generated cases are not an independent release-validation set. See [statistical rigor](docs/explanation/statistical-rigor.md) and the [three-stage workflow](docs/explanation/three-stage-workflow.md) for the method and limits.
|
|
120
131
|
|
|
121
|
-
Project evaluation and observation evidence lives under `.omk/`;
|
|
132
|
+
Project evaluation and observation evidence lives under `.omk/`; local tasks default to `.omk/eval`, with `--output` selecting another report and diagnostic directory. Machine-level state lives under `~/.oh-my-knowledge/`. Extracted knowledge items live in the explicitly selected knowledge workspace. The current version neither reads nor migrates the old storage layout. Back up before upgrading and follow the [migration guide](docs/guides/v1-preview-migration.md) to establish new evidence.
|
|
122
133
|
|
|
123
134
|
## Documentation
|
|
124
135
|
|
|
@@ -150,6 +161,7 @@ This tool is designed for **local trusted environments** (dev machines, CI pipel
|
|
|
150
161
|
| Feature | Risk | Scope |
|
|
151
162
|
|---|---|---|
|
|
152
163
|
| **Custom assertions** (`custom`) | dynamically loads and executes user-specified `.mjs` files | only use assertion files you authored or reviewed |
|
|
164
|
+
| **Local tasks and verifiers** | Executors and independent verifiers run local code; raw artifacts / logs may contain sensitive information | Use reviewed tasks, inspect file lists and output directories; copies are not security sandboxes |
|
|
153
165
|
| **eval-samples.json** | assertion configs can reference external file paths | don't use sample files from untrusted sources |
|
|
154
166
|
|
|
155
167
|
**Recommendations:**
|
package/README.zh.md
CHANGED
|
@@ -12,12 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
OMK 是面向 AI 应用作者与维护者的知识观测、评测与改进工具体系。查看真实任务如何执行,从工作日志提炼可复核的知识内容,再测量 prompt、RAG、skill、agent 或 workflow 的载体改动是否有效,为采纳、回退和继续改进提供证据。
|
|
14
14
|
|
|
15
|
-
**Observe** 保留真实表现与来源,**Measure**
|
|
15
|
+
**Observe** 保留真实表现与来源,**Measure** 固定模型与用例,控制并记录运行条件,比较知识载体,**Know** 让决定可以回到证据核对。已有两版载体时,可以直接开始评测;不必先收集日志。
|
|
16
16
|
|
|
17
17
|
当前 1.0 仍处于 **Beta 迭代期**,接口与存储契约可能继续变化。从旧版本升级前,请先阅读[迁移指南](docs/zh/guides/v1-preview-migration.md)。
|
|
18
18
|
|
|
19
19
|

|
|
20
20
|
|
|
21
|
+
图示描述可组合的工作路径:知识需复核并人工落实为载体改动,完整关联与自动闭环仍有待实现部分。
|
|
22
|
+
|
|
21
23
|
## 选择使用方式
|
|
22
24
|
|
|
23
25
|
| 入口 | 适合什么场景 |
|
|
@@ -35,6 +37,7 @@ OMK 是面向 AI 应用作者与维护者的知识观测、评测与改进工具
|
|
|
35
37
|
| 把工作经验沉淀为知识 | [从日志提炼知识](docs/zh/guides/extract-knowledge.md) | 带来源和适用条件的候选知识,支持修订、保留和舍弃 |
|
|
36
38
|
| 记录已确认的知识问题 | [MCP 主动反馈](docs/zh/guides/mcp-integration.md) | 待复核 observation;确认真实问题后再草拟用例 |
|
|
37
39
|
| 判断一版改动是否值得采用 | [评测快速上手](docs/zh/quickstart-skill-eval.md) | 版本判定、不确定性、失败用例和评分依据 |
|
|
40
|
+
| 验证 Agent 是否真正完成任务 | [本地任务验收](docs/zh/guides/local-tasks.md) | 修改文件、独立验收结果,以及失败/超时/取消后的现场证据 |
|
|
38
41
|
| 自动尝试改进 skill | [迭代改进](docs/zh/guides/auto-improve-skills.md) | 经受控比较筛选的候选版本 |
|
|
39
42
|
| 将评测接入服务或平台 | [Node.js 服务](docs/zh/guides/eval-runtime.md) · [平台宿主](docs/zh/guides/platform-host-integration.md) | 可组合的执行、评分与比较接口 |
|
|
40
43
|
|
|
@@ -46,6 +49,8 @@ OMK 是面向 AI 应用作者与维护者的知识观测、评测与改进工具
|
|
|
46
49
|
npm i -g oh-my-knowledge@next
|
|
47
50
|
```
|
|
48
51
|
|
|
52
|
+
本地任务验收需要 `1.0.0-beta.13` 或更新版本。安装后用 `omk --version` 核对版本,再按[本地任务指南](docs/zh/guides/local-tasks.md)运行可信示例。
|
|
53
|
+
|
|
49
54
|
### 先看已有任务
|
|
50
55
|
|
|
51
56
|
```bash
|
|
@@ -74,6 +79,12 @@ omk eval --control code-review-v1 --treatment code-review-v2
|
|
|
74
79
|
|
|
75
80
|
[完整教程](docs/zh/quickstart-skill-eval.md)介绍模型选择、换成自己的 skill 和结果解读;[示例画廊](examples/README.zh.md)提供更多可运行场景。
|
|
76
81
|
|
|
82
|
+
### 验证文件是否真的改对了
|
|
83
|
+
|
|
84
|
+
本地任务从声明的文件快照执行两版知识,采集文件并交给独立验收程序检查;失败、超时或取消也保存声明文件和部分日志。它测量明确提供知识内容后的效果,不测宿主原生 skill 发现。任务与验收程序必须可信,工作区副本不是安全沙箱。
|
|
85
|
+
|
|
86
|
+
[带 npm 依赖的购物车案例](examples/local-task/project/README.zh.md)记录了一次真实 Codex 运行:treatment 修改目标源码并通过六项检查,要求不编辑的负对照通过三项。该案例证明执行与验收链路可用,不证明知识版本的普遍收益;模型、用量和耗时见案例记录。[本地任务指南](docs/zh/guides/local-tasks.md)提供安装、配置与证据读取入口。
|
|
87
|
+
|
|
77
88
|
## 在 Agent 与 DSH 中使用
|
|
78
89
|
|
|
79
90
|
### Agent Skill 与 MCP
|
|
@@ -116,9 +127,9 @@ dsh --profile web
|
|
|
116
127
|
|
|
117
128
|
这是可组合的工作路径,不是强制的单向状态机。一次评测通过也不等于知识已写入或版本已发布。
|
|
118
129
|
|
|
119
|
-
|
|
130
|
+
固定配置不能保证外部模型服务或重跑结果完全一致。结论受用例、评分准则和执行环境约束。观测信号不等于因果结论,生成的样本也不能直接充当独立发布验证集。原理与限制见[统计严谨性](docs/zh/explanation/statistical-rigor.md)和[三阶段工作流](docs/zh/explanation/three-stage-workflow.md)。
|
|
120
131
|
|
|
121
|
-
项目评测与观测证据保存在 `.omk
|
|
132
|
+
项目评测与观测证据保存在 `.omk/`,本地任务默认写入 `.omk/eval`,也可通过 `--output` 指定报告与诊断目录。机器级状态位于 `~/.oh-my-knowledge/`;提炼的知识内容保存在显式选择的知识工作区。当前版本不读取或迁移旧存储布局;升级前备份,按[迁移指南](docs/zh/guides/v1-preview-migration.md)重新建立证据。
|
|
122
133
|
|
|
123
134
|
## 文档
|
|
124
135
|
|
|
@@ -150,6 +161,7 @@ dsh --profile web
|
|
|
150
161
|
| 功能 | 风险 | 适用场景 |
|
|
151
162
|
|------|------|----------|
|
|
152
163
|
| **自定义断言**(`custom`) | 动态加载并执行用户指定的 `.mjs` 文件 | 仅使用自己编写或审查过的断言文件 |
|
|
164
|
+
| **本地任务与验收程序** | 执行器与独立验收会运行本地代码,原始产物/日志可能含敏感信息 | 仅运行已审查的任务,核对文件清单与输出目录;副本不提供安全沙箱 |
|
|
153
165
|
| **eval-samples.json** | 断言配置可引用外部文件路径 | 不要使用来源不明的用例文件 |
|
|
154
166
|
|
|
155
167
|
**建议:**
|
|
@@ -247,6 +247,26 @@ omk eval gold validate <dir> [flags]
|
|
|
247
247
|
|
|
248
248
|
- `--lang` `option`:输出语言 zh|en,优先级 CLI > OMK_LANG env > 全局设置 > 系统 locale > zh。
|
|
249
249
|
|
|
250
|
+
## omk eval task
|
|
251
|
+
|
|
252
|
+
从固定快照执行可信本地任务 A/B,采集产物并独立验收。
|
|
253
|
+
|
|
254
|
+
**用法:**
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
omk eval task <definition> [flags]
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**参数:**
|
|
261
|
+
|
|
262
|
+
- `definition`(必填):本地任务 YAML/JSON 定义。
|
|
263
|
+
|
|
264
|
+
**Flags:**
|
|
265
|
+
|
|
266
|
+
- `--dry-run` `boolean`:预览封存计划,不执行任务。
|
|
267
|
+
- `--lang` `option`:输出语言 zh|en,优先级 CLI > OMK_LANG env > 全局设置 > 系统 locale > zh。
|
|
268
|
+
- `--output` `option` (默认 `.omk/eval`):报告输出目录。
|
|
269
|
+
|
|
250
270
|
## omk evolve
|
|
251
271
|
|
|
252
272
|
自动迭代改进 skill:多轮 eval + skill 重写,直到达到 --target 或耗尽 --rounds。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from '../../oclif/base-command.js';
|
|
2
|
+
export default class EvalTask extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
definition: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static flags: {
|
|
8
|
+
lang: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir, homedir } from 'node:os';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { BaseCommand } from '../../oclif/base-command.js';
|
|
6
|
+
import { LANG_FLAG, bilingual } from '../../oclif/i18n.js';
|
|
7
|
+
import { prepareLocalTask } from '../../../eval-workflows/hosts/local-task/application.js';
|
|
8
|
+
export default class EvalTask extends BaseCommand {
|
|
9
|
+
static description = bilingual({ zh: '从固定快照执行可信本地任务 A/B,采集产物并独立验收。', en: 'Run trusted local task A/B from a fixed snapshot, collect artifacts and verify outcomes independently.' });
|
|
10
|
+
static args = { definition: Args.string({ required: true, description: bilingual({ zh: '本地任务 YAML/JSON 定义。', en: 'Local task YAML/JSON definition.' }) }) };
|
|
11
|
+
static flags = {
|
|
12
|
+
lang: LANG_FLAG,
|
|
13
|
+
'dry-run': Flags.boolean({ description: bilingual({ zh: '预览封存计划,不执行任务。', en: 'Preview the sealed plan without executing tasks.' }) }),
|
|
14
|
+
output: Flags.string({ default: '.omk/eval', description: bilingual({ zh: '报告输出目录。', en: 'Report output directory.' }) }),
|
|
15
|
+
};
|
|
16
|
+
async run() {
|
|
17
|
+
const { args, flags } = await this.parse(EvalTask);
|
|
18
|
+
const temporaryRoot = await mkdtemp(join(tmpdir(), 'omk-local-task-'));
|
|
19
|
+
try {
|
|
20
|
+
await this.runWithCancellation(async (signal) => {
|
|
21
|
+
const application = await prepareLocalTask({ definitionPath: resolve(args.definition), temporaryRoot,
|
|
22
|
+
outputDirectory: resolve(flags.output),
|
|
23
|
+
authenticationFile: join(process.env.CODEX_HOME ?? join(homedir(), '.codex'), 'auth.json'),
|
|
24
|
+
});
|
|
25
|
+
if (flags['dry-run']) {
|
|
26
|
+
this.log(JSON.stringify(application.preview, null, 2));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
process.stderr.write(`${JSON.stringify(application.preview, null, 2)}\n`);
|
|
30
|
+
const { result, artifacts, reference, diagnosticsDirectory, diagnosticReferences } = await application.run(signal);
|
|
31
|
+
this.log(JSON.stringify({ runId: result.runId, outputDirectory: resolve(flags.output), reference, diagnosticsDirectory, diagnosticReferences, report: artifacts.report }, null, 2));
|
|
32
|
+
if (result.status !== 'completed' || artifacts.execution.records.some((record) => record.executionStatus !== 'completed')
|
|
33
|
+
|| artifacts.evaluation.records.some((record) => record.evaluationStatus !== 'completed'
|
|
34
|
+
|| record.observations.some((observation) => observation.observationStatus !== 'observed')))
|
|
35
|
+
this.exit(1);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
await rm(temporaryRoot, { recursive: true, force: true });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CustomEvaluator } from '../../../eval-runtime/custom-evaluator.js';
|
|
2
|
+
import { type LocalTaskDefinition, type TaskFile } from '../../inputs/contracts/local-task.js';
|
|
3
|
+
import type { TaskAttemptDiagnostic } from './diagnostics.js';
|
|
4
|
+
import { type CapturedIdentityFile } from '../adapters/shared/content-identity.js';
|
|
5
|
+
export declare const TASK_METRIC_ID = "task-accepted";
|
|
6
|
+
export declare function createTaskAcceptance(input: {
|
|
7
|
+
definition: LocalTaskDefinition['acceptance'];
|
|
8
|
+
files: readonly TaskFile[];
|
|
9
|
+
requiredArtifacts: readonly string[];
|
|
10
|
+
temporaryRoot: string;
|
|
11
|
+
runtimeFiles: readonly CapturedIdentityFile[];
|
|
12
|
+
captureDiagnostic: (diagnostic: TaskAttemptDiagnostic) => Promise<void>;
|
|
13
|
+
}): CustomEvaluator;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mkdtemp, mkdir, rm } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { ExecutionPortFailure } from '../../../eval-core/execution/index.js';
|
|
5
|
+
import { spawnWithSigintPropagation } from '../../../executors/core/subprocess.js';
|
|
6
|
+
import { LocalTaskAcceptanceSchema, LocalTaskOutputSchema } from '../../inputs/contracts/local-task.js';
|
|
7
|
+
import { materializeTaskFiles, taskTreeDigest } from './files.js';
|
|
8
|
+
import { assertIdentityFilesUnchanged } from '../adapters/shared/content-identity.js';
|
|
9
|
+
export const TASK_METRIC_ID = 'task-accepted';
|
|
10
|
+
export function createTaskAcceptance(input) {
|
|
11
|
+
const acceptanceDigest = taskTreeDigest(input.files);
|
|
12
|
+
const evidence = (value) => ({ value, classification: 'sensitive' });
|
|
13
|
+
const missing = (reasonCode) => ({
|
|
14
|
+
resultKind: 'completed', results: [{ metricId: TASK_METRIC_ID, resultKind: 'missing', reasonCode,
|
|
15
|
+
evidence: evidence({ acceptanceDigest, status: reasonCode }) }],
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
evaluatorKind: 'custom', evaluatorId: 'local-task-acceptance', instrumentId: 'local-task-acceptance-v1',
|
|
19
|
+
metrics: [{ metricId: TASK_METRIC_ID, valueType: 'boolean', direction: 'higher-is-better', missingPolicyId: 'exclude/v1' }],
|
|
20
|
+
bindings: [{ bindingId: 'actual', sourceKind: 'output', pointer: '' }],
|
|
21
|
+
implementation: {
|
|
22
|
+
implementationId: 'omk.local-task-acceptance/v1', version: '1.1.0',
|
|
23
|
+
schemas: {
|
|
24
|
+
bindings: z.strictObject({ actual: LocalTaskOutputSchema }),
|
|
25
|
+
values: { [TASK_METRIC_ID]: z.boolean() },
|
|
26
|
+
fingerprintFacets: { output: 'omk.local-task-output/v1', acceptance: 'omk.local-task-acceptance/v1' },
|
|
27
|
+
},
|
|
28
|
+
fingerprintFacets: { acceptanceDigest, nodeVersion: process.version, entrypoint: input.definition.entrypoint,
|
|
29
|
+
runtimeFiles: input.runtimeFiles.map(({ facetId, digest, size }) => ({ facetId, digest, size })),
|
|
30
|
+
platform: process.platform, architecture: process.arch,
|
|
31
|
+
environment: { LANG: 'C', TZ: 'UTC', inheritance: 'none', home: 'acceptance-private' },
|
|
32
|
+
timeoutMs: input.definition.timeoutMs, requiredArtifacts: [...input.requiredArtifacts].sort() },
|
|
33
|
+
async evaluate({ bindings, signal }) {
|
|
34
|
+
const actual = LocalTaskOutputSchema.parse(bindings.actual);
|
|
35
|
+
if (actual.collectionErrors.length > 0 || actual.missing.length > 0
|
|
36
|
+
|| input.requiredArtifacts.some((path) => !actual.files.some((file) => file.path === path))) {
|
|
37
|
+
return missing('TASK_ARTIFACTS_MISSING');
|
|
38
|
+
}
|
|
39
|
+
signal.throwIfAborted();
|
|
40
|
+
const root = await mkdtemp(join(input.temporaryRoot, 'acceptance-'));
|
|
41
|
+
let result;
|
|
42
|
+
let stdout = '';
|
|
43
|
+
let stderr = '';
|
|
44
|
+
let processOutcome = null;
|
|
45
|
+
let failureCode;
|
|
46
|
+
try {
|
|
47
|
+
await assertIdentityFilesUnchanged(input.runtimeFiles, { adapterLabel: 'Local task acceptance',
|
|
48
|
+
cancellationCode: 'TASK_ACCEPTANCE_CANCELLED', identityChangedCode: 'TASK_ACCEPTANCE_RUNTIME_CHANGED', signal });
|
|
49
|
+
const artifacts = join(root, 'artifacts');
|
|
50
|
+
const verifier = join(root, 'verifier');
|
|
51
|
+
await mkdir(artifacts, { mode: 0o700 });
|
|
52
|
+
await mkdir(verifier, { mode: 0o700 });
|
|
53
|
+
await materializeTaskFiles(artifacts, actual.files);
|
|
54
|
+
await materializeTaskFiles(verifier, input.files);
|
|
55
|
+
const { child, done } = spawnWithSigintPropagation(process.execPath, [join(verifier, input.definition.entrypoint)], {
|
|
56
|
+
cwd: artifacts, env: { HOME: root, TMPDIR: root, LANG: 'C', TZ: 'UTC' }, timeoutMs: input.definition.timeoutMs,
|
|
57
|
+
abortSignal: signal, maxBuffer: 256 * 1024,
|
|
58
|
+
});
|
|
59
|
+
child.stdin?.on('error', () => { });
|
|
60
|
+
child.stdin?.end(JSON.stringify({ sampleId: actual.source.sampleId }));
|
|
61
|
+
const completed = await done;
|
|
62
|
+
stdout = completed.stdout;
|
|
63
|
+
stderr = completed.stderr;
|
|
64
|
+
processOutcome = { exitCode: completed.code, signal: completed.signal, failureKind: null };
|
|
65
|
+
const acceptance = LocalTaskAcceptanceSchema.safeParse(JSON.parse(completed.stdout));
|
|
66
|
+
if (!acceptance.success) {
|
|
67
|
+
failureCode = 'TASK_ACCEPTANCE_INVALID';
|
|
68
|
+
result = missing(failureCode);
|
|
69
|
+
}
|
|
70
|
+
else
|
|
71
|
+
result = { resultKind: 'completed', results: [{
|
|
72
|
+
metricId: TASK_METRIC_ID, resultKind: 'score', value: acceptance.data.passed,
|
|
73
|
+
evidence: evidence({ acceptanceDigest, ...acceptance.data }),
|
|
74
|
+
}] };
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
const failure = error;
|
|
78
|
+
stdout = failure.stdout ?? stdout;
|
|
79
|
+
stderr = failure.stderr ?? stderr;
|
|
80
|
+
if (failure.failureKind)
|
|
81
|
+
processOutcome = { exitCode: failure.code ?? null, signal: failure.signal ?? null, failureKind: failure.failureKind };
|
|
82
|
+
failureCode = signal.aborted ? 'TASK_ACCEPTANCE_CANCELLED'
|
|
83
|
+
: failure.killedByTimeout ? 'TASK_ACCEPTANCE_TIMEOUT'
|
|
84
|
+
: error instanceof ExecutionPortFailure ? 'TASK_ACCEPTANCE_RUNTIME_CHANGED'
|
|
85
|
+
: error instanceof SyntaxError ? 'TASK_ACCEPTANCE_INVALID' : 'TASK_ACCEPTANCE_FAILED';
|
|
86
|
+
result = missing(failureCode);
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
await rm(root, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
failureCode = 'TASK_ACCEPTANCE_CLEANUP_FAILED';
|
|
93
|
+
result = missing(failureCode);
|
|
94
|
+
}
|
|
95
|
+
if (failureCode)
|
|
96
|
+
await input.captureDiagnostic({ stage: 'acceptance', status: failureCode, output: actual, stdout, stderr, processOutcome });
|
|
97
|
+
return result;
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type JsonValue } from '../../../eval-core/contracts/index.js';
|
|
2
|
+
import type { EvaluationResult } from '../../../eval-runtime/evaluation/contracts.js';
|
|
3
|
+
import { type StoredCoreRunArtifacts } from '../../artifact-store/index.js';
|
|
4
|
+
import { type LocalTaskDefinition } from '../../inputs/contracts/local-task.js';
|
|
5
|
+
export interface LocalTaskOptions {
|
|
6
|
+
readonly definitionPath: string;
|
|
7
|
+
readonly temporaryRoot: string;
|
|
8
|
+
readonly outputDirectory: string;
|
|
9
|
+
/** Read only for explicit live runs; copied into an attempt-private home, never persisted. */
|
|
10
|
+
readonly authenticationFile?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Host application shared by CLI and programmatic users. All locators remain outside identities. */
|
|
13
|
+
export declare function prepareLocalTask(options: LocalTaskOptions): Promise<Readonly<{
|
|
14
|
+
preview: {
|
|
15
|
+
projectionKind: string;
|
|
16
|
+
taskId: string;
|
|
17
|
+
knowledgeMode: string;
|
|
18
|
+
runtimeKind: "codex" | "fixture";
|
|
19
|
+
runtimeVersion: string;
|
|
20
|
+
model: string;
|
|
21
|
+
effort: string;
|
|
22
|
+
executionTimeoutMs: number;
|
|
23
|
+
acceptanceTimeoutMs: number;
|
|
24
|
+
toolPolicy: string;
|
|
25
|
+
strongIsolation: boolean;
|
|
26
|
+
diagnosticsPolicy: string;
|
|
27
|
+
maxStreamBytes: number;
|
|
28
|
+
runtimeFiles: {
|
|
29
|
+
facetId: string;
|
|
30
|
+
digest: `sha256:${string}`;
|
|
31
|
+
size: number;
|
|
32
|
+
}[];
|
|
33
|
+
snapshotDigest: `sha256:${string}`;
|
|
34
|
+
acceptanceDigest: `sha256:${string}`;
|
|
35
|
+
snapshotFiles: string[];
|
|
36
|
+
artifactFiles: string[];
|
|
37
|
+
maxArtifactBytes: number;
|
|
38
|
+
samples: {
|
|
39
|
+
annotations?: JsonValue | undefined;
|
|
40
|
+
sampleId: string;
|
|
41
|
+
}[];
|
|
42
|
+
trust: "trusted-local";
|
|
43
|
+
writeBoundary: string;
|
|
44
|
+
retryState: string;
|
|
45
|
+
estimatedCostUSD: null;
|
|
46
|
+
estimatedWork: {
|
|
47
|
+
uncertain: ("provider-cost" | "active-duration" | "wall-clock" | "retries" | "early-termination")[];
|
|
48
|
+
sampleCount: number;
|
|
49
|
+
variantCount: number;
|
|
50
|
+
trialCount: number;
|
|
51
|
+
executionCoordinates: number;
|
|
52
|
+
evaluationCoordinates: number;
|
|
53
|
+
plannedInvocations: number;
|
|
54
|
+
};
|
|
55
|
+
limitations: string[];
|
|
56
|
+
};
|
|
57
|
+
run(signal?: AbortSignal): Promise<{
|
|
58
|
+
result: {
|
|
59
|
+
status: "completed" | "cancelled" | "budget-exhausted";
|
|
60
|
+
artifacts: import("../../../eval-core/facade.js").EvaluationRunArtifacts;
|
|
61
|
+
report: import("../../../eval-core/contracts/artifacts.js").EvaluationReport;
|
|
62
|
+
} & Readonly<{
|
|
63
|
+
runId: string;
|
|
64
|
+
definition: import("../../../eval-runtime/evaluation/contracts.js").PreparedEvaluationPlan["definition"];
|
|
65
|
+
policy: import("../../../eval-runtime/evaluation/contracts.js").PreparedEvaluationPlan["measurementPolicy"];
|
|
66
|
+
analysisResults: Readonly<Record<string, import("../../../eval-core/contracts/artifacts.js").AnalysisRecord>>;
|
|
67
|
+
}>;
|
|
68
|
+
artifacts: StoredCoreRunArtifacts;
|
|
69
|
+
reference: {
|
|
70
|
+
mediaType: string;
|
|
71
|
+
digest: string;
|
|
72
|
+
size?: number | undefined;
|
|
73
|
+
uri?: string | undefined;
|
|
74
|
+
};
|
|
75
|
+
diagnosticsDirectory: string;
|
|
76
|
+
diagnosticReferences: {
|
|
77
|
+
mediaType: string;
|
|
78
|
+
digest: string;
|
|
79
|
+
size?: number | undefined;
|
|
80
|
+
uri?: string | undefined;
|
|
81
|
+
}[];
|
|
82
|
+
}>;
|
|
83
|
+
rescore(source: EvaluationResult, acceptance: LocalTaskDefinition["acceptance"], root: string, signal?: AbortSignal): Promise<{
|
|
84
|
+
result: EvaluationResult;
|
|
85
|
+
reference: {
|
|
86
|
+
mediaType: string;
|
|
87
|
+
digest: string;
|
|
88
|
+
size?: number | undefined;
|
|
89
|
+
uri?: string | undefined;
|
|
90
|
+
};
|
|
91
|
+
diagnosticsDirectory: string;
|
|
92
|
+
diagnosticReferences: {
|
|
93
|
+
mediaType: string;
|
|
94
|
+
digest: string;
|
|
95
|
+
size?: number | undefined;
|
|
96
|
+
uri?: string | undefined;
|
|
97
|
+
}[];
|
|
98
|
+
}>;
|
|
99
|
+
}>>;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdtemp, readFile, realpath, rm } from 'node:fs/promises';
|
|
3
|
+
import { delimiter, dirname, join, resolve } from 'node:path';
|
|
4
|
+
import yaml from 'js-yaml';
|
|
5
|
+
import { deepFreezeCanonicalJson, digestCanonicalJson } from '../../../eval-core/contracts/index.js';
|
|
6
|
+
import { prepareEvaluation } from '../../../eval-runtime/evaluation/prepare.js';
|
|
7
|
+
import { rescore } from '../../../eval-runtime/evaluation/reuse.js';
|
|
8
|
+
import { saveEvaluationResult } from '../../../eval-runtime/result-store.js';
|
|
9
|
+
import { createNodeCoreContentStore, createNodeCoreRunArtifactStore } from '../../artifact-store/index.js';
|
|
10
|
+
import { persistCoreArtifactSidecars } from '../../orchestration/artifact-graph-persistence.js';
|
|
11
|
+
import { LocalTaskDefinitionSchema, LocalTaskOutputSchema } from '../../inputs/contracts/local-task.js';
|
|
12
|
+
import { captureTaskFiles, fileDigest, taskTreeDigest } from './files.js';
|
|
13
|
+
import { createTaskDiagnostics } from './diagnostics.js';
|
|
14
|
+
import { createTaskWorkspace } from './workspace.js';
|
|
15
|
+
import { createTaskExecutor } from './executor.js';
|
|
16
|
+
import { createTaskAcceptance, TASK_METRIC_ID } from './acceptance.js';
|
|
17
|
+
import { probeCodexCliVersion } from '../adapters/codex/version.js';
|
|
18
|
+
import { captureIdentityFiles } from '../adapters/shared/content-identity.js';
|
|
19
|
+
function assertSupported(task) {
|
|
20
|
+
if (task.samples.requires !== undefined)
|
|
21
|
+
throw new TypeError('TASK_REQUIREMENTS_UNSUPPORTED');
|
|
22
|
+
for (const sample of task.samples.samples) {
|
|
23
|
+
if (sample.input.inputKind !== 'text' || sample.executionContext !== undefined
|
|
24
|
+
|| sample.evaluationContext !== undefined || sample.expected !== undefined) {
|
|
25
|
+
throw new TypeError('Local tasks currently accept text sample v3 input and annotations; acceptance belongs in the independent verifier.');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Host application shared by CLI and programmatic users. All locators remain outside identities. */
|
|
30
|
+
export async function prepareLocalTask(options) {
|
|
31
|
+
const base = dirname(resolve(options.definitionPath));
|
|
32
|
+
const task = LocalTaskDefinitionSchema.parse(yaml.load(await readFile(options.definitionPath, 'utf8')));
|
|
33
|
+
assertSupported(task);
|
|
34
|
+
const snapshotRoot = await realpath(resolve(base, task.snapshot.root));
|
|
35
|
+
const acceptanceRoot = await realpath(resolve(base, task.acceptance.root));
|
|
36
|
+
const snapshotPaths = new Set(task.snapshot.files.map((file) => join(snapshotRoot, file)));
|
|
37
|
+
if (task.acceptance.files.some((file) => snapshotPaths.has(join(acceptanceRoot, file)))) {
|
|
38
|
+
throw new TypeError('TASK_ACCEPTANCE_EXPOSED_IN_SNAPSHOT');
|
|
39
|
+
}
|
|
40
|
+
const [snapshot, acceptanceFiles, control, treatment] = await Promise.all([
|
|
41
|
+
captureTaskFiles(snapshotRoot, task.snapshot.files, task.artifacts.maxBytes),
|
|
42
|
+
captureTaskFiles(acceptanceRoot, task.acceptance.files, 16 * 1024 * 1024),
|
|
43
|
+
readFile(resolve(base, task.variants.control), 'utf8'),
|
|
44
|
+
readFile(resolve(base, task.variants.treatment), 'utf8'),
|
|
45
|
+
]);
|
|
46
|
+
if (control.trim() === '' || treatment.trim() === '')
|
|
47
|
+
throw new TypeError('TASK_KNOWLEDGE_EMPTY');
|
|
48
|
+
const execution = task.execution;
|
|
49
|
+
const executable = execution.runtimeKind === 'fixture' ? process.execPath : await realpath(resolve(base, execution.executable));
|
|
50
|
+
const fixtureSource = execution.runtimeKind === 'fixture' ? await readFile(resolve(base, execution.script), 'utf8') : undefined;
|
|
51
|
+
const identityFiles = await captureIdentityFiles([
|
|
52
|
+
{ facetId: 'node-runtime', path: process.execPath },
|
|
53
|
+
...(execution.runtimeKind === 'codex' ? [{ facetId: 'codex-executable', path: executable },
|
|
54
|
+
...execution.identityFiles.map((file) => ({ ...file, path: resolve(base, file.path) }))] : []),
|
|
55
|
+
], 'Local task');
|
|
56
|
+
const executableDigest = fixtureSource === undefined
|
|
57
|
+
? identityFiles.find((file) => file.facetId === 'codex-executable').digest : fileDigest(Buffer.from(fixtureSource));
|
|
58
|
+
let version = process.version;
|
|
59
|
+
if (execution.runtimeKind === 'codex') {
|
|
60
|
+
const probeHome = await mkdtemp(join(options.temporaryRoot, 'probe-'));
|
|
61
|
+
try {
|
|
62
|
+
version = await probeCodexCliVersion({ executablePath: executable,
|
|
63
|
+
environment: { PATH: [dirname(process.execPath), '/usr/bin', '/bin'].join(delimiter),
|
|
64
|
+
HOME: probeHome, CODEX_HOME: probeHome, TMPDIR: probeHome, LANG: 'C', TZ: 'UTC' },
|
|
65
|
+
maxOutputBytes: 65536, timeoutMs: 5000 });
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
await rm(probeHome, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const workspace = createTaskWorkspace(snapshot, options.temporaryRoot);
|
|
72
|
+
const contentStore = createNodeCoreContentStore(join(options.outputDirectory, 'content'));
|
|
73
|
+
const diagnostics = createTaskDiagnostics(contentStore, options.outputDirectory);
|
|
74
|
+
const executor = createTaskExecutor({ captureDiagnostic: diagnostics.capture, definition: task, snapshot, provider: workspace.provider,
|
|
75
|
+
temporaryRoot: options.temporaryRoot, executable, executableDigest, version, identityFiles,
|
|
76
|
+
...(fixtureSource === undefined ? {} : { fixtureSource }),
|
|
77
|
+
...(options.authenticationFile === undefined ? {} : { authenticationFile: options.authenticationFile }),
|
|
78
|
+
});
|
|
79
|
+
const store = createNodeCoreRunArtifactStore(options.outputDirectory, { contentResolver: contentStore });
|
|
80
|
+
const declaration = {
|
|
81
|
+
dataset: { datasetId: task.taskId, samples: task.samples.samples.map((sample) => ({
|
|
82
|
+
sampleId: sample.sampleId, input: sample.input,
|
|
83
|
+
...(sample.annotations === undefined ? {} : { annotations: sample.annotations }),
|
|
84
|
+
})) },
|
|
85
|
+
variants: ['control', 'treatment'].map((variantId) => ({
|
|
86
|
+
variantId, artifact: { name: variantId, kind: 'skill', source: 'inline', content: variantId === 'control' ? control : treatment },
|
|
87
|
+
execution: { executor, workspace: workspace.descriptor },
|
|
88
|
+
})),
|
|
89
|
+
evaluators: [createTaskAcceptance({ captureDiagnostic: diagnostics.capture, definition: task.acceptance, files: acceptanceFiles,
|
|
90
|
+
requiredArtifacts: task.artifacts.files, temporaryRoot: options.temporaryRoot,
|
|
91
|
+
runtimeFiles: identityFiles.filter((file) => file.facetId === 'node-runtime') })],
|
|
92
|
+
comparisons: [{ comparisonId: 'task-ab', controlVariantId: 'control', treatmentVariantIds: ['treatment'], metricIds: [TASK_METRIC_ID] }],
|
|
93
|
+
analyses: [{ analysisId: 'task-difference', analysisKind: 'comparison-interval', statistic: 'mean-difference',
|
|
94
|
+
comparisonId: 'task-ab', treatmentVariantId: 'treatment', metricId: TASK_METRIC_ID,
|
|
95
|
+
confidence: { method: 'percentile-bootstrap', level: 0.95, resamples: 1000 } }],
|
|
96
|
+
decision: { decisionKind: 'analysis', analysisId: 'task-difference', minimumEvidenceStatus: 'complete' },
|
|
97
|
+
experiment: { seed: task.seed, trials: task.trials, sampling: { samplingKind: 'paired', seedCoupling: 'uncontrolled' } },
|
|
98
|
+
policy: { execution: { maxConcurrency: 1, timeoutMs: execution.timeoutMs }, evaluation: { maxConcurrency: 1 },
|
|
99
|
+
evidence: { output: 'full', trace: 'full', evaluatorEvidence: 'full', maximumClassification: 'sensitive' } },
|
|
100
|
+
infrastructure: { contentStore, contentResolver: contentStore },
|
|
101
|
+
};
|
|
102
|
+
const prepared = await prepareEvaluation(declaration);
|
|
103
|
+
async function archiveFiles(files, mediaType) {
|
|
104
|
+
return contentStore.put({ value: [...files], digest: taskTreeDigest(files), mediaType, classification: 'sensitive' });
|
|
105
|
+
}
|
|
106
|
+
async function persist(result, plan = prepared.plan) {
|
|
107
|
+
const { execution: executed, evaluation, analysis } = result.artifacts ?? {};
|
|
108
|
+
if (!executed || !evaluation || !analysis || !result.report)
|
|
109
|
+
throw new Error(`TASK_RUN_INCOMPLETE:${result.status}`);
|
|
110
|
+
const artifacts = await store.save({ runId: result.runId, createdAt: new Date().toISOString(),
|
|
111
|
+
plan, execution: executed, evaluation, analysis, report: result.report });
|
|
112
|
+
await persistCoreArtifactSidecars({ source: artifacts, outputDirectory: options.outputDirectory, cwd: base });
|
|
113
|
+
return artifacts;
|
|
114
|
+
}
|
|
115
|
+
const preview = deepFreezeCanonicalJson({
|
|
116
|
+
projectionKind: 'local-task-preview', taskId: task.taskId, knowledgeMode: 'provided-content',
|
|
117
|
+
runtimeKind: execution.runtimeKind, runtimeVersion: version,
|
|
118
|
+
model: execution.runtimeKind === 'codex' ? execution.model : 'offline-fixture',
|
|
119
|
+
effort: execution.runtimeKind === 'codex' ? execution.effort : 'none',
|
|
120
|
+
executionTimeoutMs: execution.timeoutMs, acceptanceTimeoutMs: task.acceptance.timeoutMs,
|
|
121
|
+
toolPolicy: 'runtime-default', strongIsolation: false,
|
|
122
|
+
diagnosticsPolicy: 'attempt-snapshot/v1', maxStreamBytes: 10 * 1024 * 1024,
|
|
123
|
+
runtimeFiles: identityFiles.map(({ facetId, digest, size }) => ({ facetId, digest, size })),
|
|
124
|
+
snapshotDigest: taskTreeDigest(snapshot), acceptanceDigest: taskTreeDigest(acceptanceFiles),
|
|
125
|
+
snapshotFiles: task.snapshot.files, artifactFiles: task.artifacts.files, maxArtifactBytes: task.artifacts.maxBytes,
|
|
126
|
+
samples: task.samples.samples.map(({ sampleId, annotations }) => ({ sampleId, ...(annotations === undefined ? {} : { annotations: annotations }) })),
|
|
127
|
+
trust: task.trust, writeBoundary: 'trial-private-workspace', retryState: 'shared-within-trial',
|
|
128
|
+
estimatedCostUSD: null, estimatedWork: { ...prepared.estimatedWork, uncertain: [...prepared.estimatedWork.uncertain] },
|
|
129
|
+
limitations: ['trusted-local-not-hostile-code-sandbox', 'native-skill-discovery-not-measured',
|
|
130
|
+
'external-provider-conditions-uncontrolled', 'small-example-is-not-release-evidence'],
|
|
131
|
+
});
|
|
132
|
+
return Object.freeze({
|
|
133
|
+
preview,
|
|
134
|
+
async run(signal) {
|
|
135
|
+
const snapshotReference = await archiveFiles(snapshot, workspace.descriptor.mediaType);
|
|
136
|
+
const acceptanceReference = await archiveFiles(acceptanceFiles, 'application/vnd.omk.local-task-verifier');
|
|
137
|
+
const runId = `run-${randomUUID()}`;
|
|
138
|
+
const diagnosticsDirectory = `task-diagnostics/${runId}`;
|
|
139
|
+
const { value: result, references: diagnosticReferences } = await diagnostics.run(runId, () => prepared.run({ runId, signal, annotations: { ...preview, snapshotReference, acceptanceReference, diagnosticsDirectory } }));
|
|
140
|
+
if (result.status === 'failed') {
|
|
141
|
+
const value = JSON.parse(JSON.stringify(result));
|
|
142
|
+
const failureReference = await contentStore.put({ value, digest: digestCanonicalJson(value), mediaType: 'application/json', classification: 'sensitive' });
|
|
143
|
+
throw new Error(`TASK_RUN_FAILED:${result.error.code}:${failureReference.uri}`);
|
|
144
|
+
}
|
|
145
|
+
const reference = await saveEvaluationResult({ result, store: contentStore });
|
|
146
|
+
return { result, artifacts: await persist(result), reference, diagnosticsDirectory, diagnosticReferences };
|
|
147
|
+
},
|
|
148
|
+
async rescore(source, acceptance, root, signal) {
|
|
149
|
+
const verifiedAcceptance = LocalTaskDefinitionSchema.shape.acceptance.parse(acceptance);
|
|
150
|
+
const records = source.artifacts?.execution?.records;
|
|
151
|
+
if (!records || records.length === 0)
|
|
152
|
+
throw new TypeError('TASK_RESCORE_EVIDENCE_MISSING');
|
|
153
|
+
for (const record of records) {
|
|
154
|
+
if (record.executionStatus !== 'completed' || record.output?.contentKind !== 'inline')
|
|
155
|
+
throw new TypeError('TASK_RESCORE_EVIDENCE_MISSING');
|
|
156
|
+
const output = LocalTaskOutputSchema.parse(record.output.value);
|
|
157
|
+
if (output.missing.length || output.collectionErrors.length
|
|
158
|
+
|| task.artifacts.files.some((path) => !output.files.some((file) => file.path === path)))
|
|
159
|
+
throw new TypeError('TASK_RESCORE_EVIDENCE_MISSING');
|
|
160
|
+
}
|
|
161
|
+
const files = await captureTaskFiles(resolve(root, verifiedAcceptance.root), verifiedAcceptance.files, 16 * 1024 * 1024);
|
|
162
|
+
const nextInput = { ...declaration,
|
|
163
|
+
evaluators: [createTaskAcceptance({ captureDiagnostic: diagnostics.capture, definition: verifiedAcceptance, files, requiredArtifacts: task.artifacts.files,
|
|
164
|
+
temporaryRoot: options.temporaryRoot, runtimeFiles: identityFiles.filter((file) => file.facetId === 'node-runtime') })] };
|
|
165
|
+
const acceptanceReference = await archiveFiles(files, 'application/vnd.omk.local-task-verifier');
|
|
166
|
+
const runId = `run-${randomUUID()}`;
|
|
167
|
+
const diagnosticsDirectory = `task-diagnostics/${runId}`;
|
|
168
|
+
const { value: result, references: diagnosticReferences } = await diagnostics.run(runId, () => rescore(nextInput, source, { runId, signal, annotations: { ...preview, diagnosticsDirectory,
|
|
169
|
+
acceptanceDigest: taskTreeDigest(files), acceptanceTimeoutMs: verifiedAcceptance.timeoutMs,
|
|
170
|
+
acceptanceReference, postHocRescore: true, sourceRunId: source.runId } }));
|
|
171
|
+
// The existing run-artifact store requires a single run contract. A reused Execution
|
|
172
|
+
// retains its original contract, so persist suffix runs through Runtime's result store.
|
|
173
|
+
const reference = await saveEvaluationResult({ result, store: contentStore });
|
|
174
|
+
return { result, reference, diagnosticsDirectory, diagnosticReferences };
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
}
|