@pi-in-go/pigpen-pi-typesafe 0.1.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.
Files changed (189) hide show
  1. package/CREDITS.md +14 -0
  2. package/LICENSE +22 -0
  3. package/README.md +45 -0
  4. package/extensions/pi-typesafe/branches_test.go +185 -0
  5. package/extensions/pi-typesafe/command.go +319 -0
  6. package/extensions/pi-typesafe/export_test.go +9 -0
  7. package/extensions/pi-typesafe/extension.go +188 -0
  8. package/extensions/pi-typesafe/extension_test.go +321 -0
  9. package/extensions/pi-typesafe/fakehost_test.go +548 -0
  10. package/extensions/pi-typesafe/format.go +191 -0
  11. package/extensions/pi-typesafe/format_test.go +75 -0
  12. package/extensions/pi-typesafe/go.mod +10 -0
  13. package/extensions/pi-typesafe/go.sum +2 -0
  14. package/extensions/pi-typesafe/go.work +11 -0
  15. package/extensions/pi-typesafe/harness_test.go +200 -0
  16. package/extensions/pi-typesafe/ownmodel_test.go +100 -0
  17. package/extensions/pi-typesafe/review_test.go +134 -0
  18. package/extensions/pi-typesafe/tool.go +193 -0
  19. package/extensions/pi-typesafe/twin_test.go +28 -0
  20. package/libs/pi-typesafe-api/CREDITS.md +14 -0
  21. package/libs/pi-typesafe-api/LICENSE +22 -0
  22. package/libs/pi-typesafe-api/README.md +30 -0
  23. package/libs/pi-typesafe-api/ask.go +62 -0
  24. package/libs/pi-typesafe-api/ask_test.go +76 -0
  25. package/libs/pi-typesafe-api/auth.go +249 -0
  26. package/libs/pi-typesafe-api/auth_test.go +131 -0
  27. package/libs/pi-typesafe-api/backends.go +336 -0
  28. package/libs/pi-typesafe-api/backends_test.go +404 -0
  29. package/libs/pi-typesafe-api/batch.go +202 -0
  30. package/libs/pi-typesafe-api/batch_test.go +202 -0
  31. package/libs/pi-typesafe-api/battery_test.go +41 -0
  32. package/libs/pi-typesafe-api/calibrate.go +354 -0
  33. package/libs/pi-typesafe-api/calibrate_test.go +186 -0
  34. package/libs/pi-typesafe-api/client.go +615 -0
  35. package/libs/pi-typesafe-api/client_test.go +490 -0
  36. package/libs/pi-typesafe-api/credentials.go +252 -0
  37. package/libs/pi-typesafe-api/credentials_test.go +216 -0
  38. package/libs/pi-typesafe-api/doc.go +14 -0
  39. package/libs/pi-typesafe-api/errors.go +143 -0
  40. package/libs/pi-typesafe-api/evaluation.go +86 -0
  41. package/libs/pi-typesafe-api/evaluation_schema.json +264 -0
  42. package/libs/pi-typesafe-api/gaps_test.go +77 -0
  43. package/libs/pi-typesafe-api/go.mod +9 -0
  44. package/libs/pi-typesafe-api/go.sum +2 -0
  45. package/libs/pi-typesafe-api/helpers_test.go +169 -0
  46. package/libs/pi-typesafe-api/hostmodel/hostmodel.go +87 -0
  47. package/libs/pi-typesafe-api/json.go +299 -0
  48. package/libs/pi-typesafe-api/json_test.go +92 -0
  49. package/libs/pi-typesafe-api/ownmodel_test.go +79 -0
  50. package/libs/pi-typesafe-api/package.json +40 -0
  51. package/libs/pi-typesafe-api/provenance.json +18 -0
  52. package/libs/pi-typesafe-api/review_test.go +23 -0
  53. package/libs/pi-typesafe-api/schema.go +473 -0
  54. package/libs/pi-typesafe-api/schema_test.go +262 -0
  55. package/libs/pi-typesafe-api/testdata/tools/typebox-messages.mts +5 -0
  56. package/libs/pi-typesafe-api/testdata/typebox-messages.json +285 -0
  57. package/libs/pi-typesafe-api/twin_test.go +28 -0
  58. package/libs/pi-typesafe-api/ui/fakehost_test.go +548 -0
  59. package/libs/pi-typesafe-api/ui/keyprompt.go +115 -0
  60. package/libs/pi-typesafe-api/ui/login.go +106 -0
  61. package/libs/pi-typesafe-api/ui/twin_test.go +28 -0
  62. package/libs/pi-typesafe-api/ui/ui_test.go +285 -0
  63. package/libs/pi-typesafe-api/usage.go +366 -0
  64. package/libs/pi-typesafe-api/usage_test.go +139 -0
  65. package/libs/typesafe/CONTRACT.md +125 -0
  66. package/libs/typesafe/CREDITS.md +37 -0
  67. package/libs/typesafe/LICENSE +23 -0
  68. package/libs/typesafe/README.md +19 -0
  69. package/libs/typesafe/go.mod +3 -0
  70. package/libs/typesafe/libraries/ownmodel/backend_test.go +496 -0
  71. package/libs/typesafe/libraries/ownmodel/canon.go +190 -0
  72. package/libs/typesafe/libraries/ownmodel/convert.go +199 -0
  73. package/libs/typesafe/libraries/ownmodel/doc.go +15 -0
  74. package/libs/typesafe/libraries/ownmodel/equivalence_test.go +199 -0
  75. package/libs/typesafe/libraries/ownmodel/helpers_test.go +155 -0
  76. package/libs/typesafe/libraries/ownmodel/mutation_test.go +31 -0
  77. package/libs/typesafe/libraries/ownmodel/ownmodel.go +225 -0
  78. package/libs/typesafe/libraries/ownmodel/plan.go +442 -0
  79. package/libs/typesafe/libraries/ownmodel/run.go +288 -0
  80. package/libs/typesafe/libraries/ownmodel/schema_test.go +254 -0
  81. package/libs/typesafe/libraries/ownmodel/twins_test.go +169 -0
  82. package/libs/typesafe/libraries/ownmodel/utils_test.go +125 -0
  83. package/libs/typesafe/libraries/pigmodel/pigmodel.go +264 -0
  84. package/libs/typesafe/libraries/pigmodel/pigmodel_test.go +410 -0
  85. package/libs/typesafe/libraries/typesafe/answers.go +268 -0
  86. package/libs/typesafe/libraries/typesafe/api_response_test.go +113 -0
  87. package/libs/typesafe/libraries/typesafe/batch.go +80 -0
  88. package/libs/typesafe/libraries/typesafe/batch_test.go +133 -0
  89. package/libs/typesafe/libraries/typesafe/bench_test.go +71 -0
  90. package/libs/typesafe/libraries/typesafe/client.go +561 -0
  91. package/libs/typesafe/libraries/typesafe/client_test.go +495 -0
  92. package/libs/typesafe/libraries/typesafe/crosscheck_test.go +464 -0
  93. package/libs/typesafe/libraries/typesafe/crosscheck_workflowevals_test.go +219 -0
  94. package/libs/typesafe/libraries/typesafe/doc.go +27 -0
  95. package/libs/typesafe/libraries/typesafe/entry.go +142 -0
  96. package/libs/typesafe/libraries/typesafe/env.go +11 -0
  97. package/libs/typesafe/libraries/typesafe/errors.go +310 -0
  98. package/libs/typesafe/libraries/typesafe/errors_test.go +175 -0
  99. package/libs/typesafe/libraries/typesafe/helpers_test.go +294 -0
  100. package/libs/typesafe/libraries/typesafe/live_test.go +96 -0
  101. package/libs/typesafe/libraries/typesafe/logging.go +160 -0
  102. package/libs/typesafe/libraries/typesafe/logging_test.go +259 -0
  103. package/libs/typesafe/libraries/typesafe/marshal_test.go +112 -0
  104. package/libs/typesafe/libraries/typesafe/mutation_test.go +39 -0
  105. package/libs/typesafe/libraries/typesafe/questions.go +490 -0
  106. package/libs/typesafe/libraries/typesafe/questions_test.go +166 -0
  107. package/libs/typesafe/libraries/typesafe/regressions_test.go +159 -0
  108. package/libs/typesafe/libraries/typesafe/reliability_test.go +649 -0
  109. package/libs/typesafe/libraries/typesafe/retry.go +350 -0
  110. package/libs/typesafe/libraries/typesafe/retry_test.go +297 -0
  111. package/libs/typesafe/libraries/typesafe/runtime_test.go +26 -0
  112. package/libs/typesafe/libraries/typesafe/transport_test.go +163 -0
  113. package/libs/typesafe/libraries/typesafe/twins_test.go +127 -0
  114. package/libs/typesafe/libraries/typesafe/types_test.go +165 -0
  115. package/libs/typesafe/libraries/typesafe/version.go +10 -0
  116. package/libs/typesafe/package.json +37 -0
  117. package/libs/typesafe/provenance.json +49 -0
  118. package/package.json +42 -0
  119. package/port/PORT.md +98 -0
  120. package/port/accepted-gaps.json +3 -0
  121. package/port/golden/enable-confirm.jsonl +11 -0
  122. package/port/golden/enable-decline.jsonl +20 -0
  123. package/port/golden/enable-missing-key.jsonl +4 -0
  124. package/port/golden/login-shadow.jsonl +4 -0
  125. package/port/golden/logout-env-key.jsonl +6 -0
  126. package/port/golden/playground-cancel.jsonl +4 -0
  127. package/port/golden/playground-invalid-json.jsonl +5 -0
  128. package/port/golden/playground-invalid-questions.jsonl +5 -0
  129. package/port/golden/status-env-key.jsonl +6 -0
  130. package/port/golden/status-no-key.jsonl +6 -0
  131. package/port/golden/tool-disabled.jsonl +18 -0
  132. package/port/golden/trailing-words.jsonl +10 -0
  133. package/port/library-mutations.py +44 -0
  134. package/port/mutations.json +302 -0
  135. package/port/oracle/.env.example +4 -0
  136. package/port/oracle/CHANGELOG.md +91 -0
  137. package/port/oracle/CONTRIBUTING.md +35 -0
  138. package/port/oracle/LICENSE +21 -0
  139. package/port/oracle/README.md +159 -0
  140. package/port/oracle/docs/api.md +143 -0
  141. package/port/oracle/docs/ci-cd.md +97 -0
  142. package/port/oracle/examples/decision-extension.ts +41 -0
  143. package/port/oracle/extensions/index.js +2 -0
  144. package/port/oracle/package.json +89 -0
  145. package/port/oracle/scripts/dev-pi.mjs +23 -0
  146. package/port/oracle/scripts/live-smoke.mjs +35 -0
  147. package/port/oracle/src/ask.ts +42 -0
  148. package/port/oracle/src/auth.ts +171 -0
  149. package/port/oracle/src/backends.ts +196 -0
  150. package/port/oracle/src/batch.ts +170 -0
  151. package/port/oracle/src/calibrate.ts +237 -0
  152. package/port/oracle/src/client.ts +310 -0
  153. package/port/oracle/src/credentials.ts +136 -0
  154. package/port/oracle/src/errors.ts +53 -0
  155. package/port/oracle/src/extension.ts +204 -0
  156. package/port/oracle/src/index.ts +31 -0
  157. package/port/oracle/src/key-prompt.ts +51 -0
  158. package/port/oracle/src/login.ts +60 -0
  159. package/port/oracle/src/schema.ts +158 -0
  160. package/port/oracle/src/ui.ts +4 -0
  161. package/port/oracle/src/usage.ts +258 -0
  162. package/port/oracle/tests/ask.test.ts +63 -0
  163. package/port/oracle/tests/auth.test.ts +141 -0
  164. package/port/oracle/tests/backends.test.ts +380 -0
  165. package/port/oracle/tests/batch.test.ts +156 -0
  166. package/port/oracle/tests/calibrate.test.ts +144 -0
  167. package/port/oracle/tests/client.test.ts +499 -0
  168. package/port/oracle/tests/credentials.test.ts +144 -0
  169. package/port/oracle/tests/extension.test.ts +276 -0
  170. package/port/oracle/tests/key-prompt.test.ts +47 -0
  171. package/port/oracle/tests/login.test.ts +101 -0
  172. package/port/oracle/tests/schema.test.ts +85 -0
  173. package/port/oracle/tests/usage.test.ts +106 -0
  174. package/port/oracle/tsconfig.build.json +10 -0
  175. package/port/oracle/tsconfig.json +14 -0
  176. package/port/scenarios/enable-confirm.json +5 -0
  177. package/port/scenarios/enable-decline.json +3 -0
  178. package/port/scenarios/enable-missing-key.json +2 -0
  179. package/port/scenarios/login-shadow.json +2 -0
  180. package/port/scenarios/logout-env-key.json +3 -0
  181. package/port/scenarios/playground-cancel.json +2 -0
  182. package/port/scenarios/playground-invalid-json.json +2 -0
  183. package/port/scenarios/playground-invalid-questions.json +2 -0
  184. package/port/scenarios/status-env-key.json +3 -0
  185. package/port/scenarios/status-no-key.json +3 -0
  186. package/port/scenarios/tool-disabled.json +2 -0
  187. package/port/scenarios/trailing-words.json +5 -0
  188. package/port/upstream-tests.json +160 -0
  189. package/provenance.json +18 -0
package/port/PORT.md ADDED
@@ -0,0 +1,98 @@
1
+ # Port record: pi-typesafe
2
+
3
+ | Input | Identity |
4
+ |---|---|
5
+ | Original | https://github.com/DevMortimer/pi-typesafe, commit `ed439f834665ad6fc652787f5ba7c23852566d49` (0.8.0), MIT, Ryan Gapac; vendored unmodified in `port/oracle/` (no `.github`, `package-lock.json` or preview image). Baseline: `npm test` 134/134 in that directory. |
6
+ | Oracle | Pi 0.87.1 (the npm release), Node 24.19.0; the extension loaded from `port/oracle/src/extension.ts` with `npm install` dependencies (`@typesafe-ai/sdk` 0.6.0, `typebox`) |
7
+ | Target | PiG 0.3.0+0.87.1 (source commit `63c6ba456`), go1.27.1, shared client `components/typesafe` (port of `@typesafe-ai/sdk` 0.6.0 and system-one-adapter) |
8
+ | Original's tests | 134 cases in 12 files; the ledger is `port/upstream-tests.json` |
9
+
10
+ Two Packages carry the port: `components/pi-typesafe` (extension, this record) and `components/pi-typesafe-api` (the typed API).
11
+
12
+ ## File mapping
13
+
14
+ | Original (`port/oracle/`) | Go |
15
+ |---|---|
16
+ | `src/index.ts` (exports) | the exported API of `pi-typesafe-api` (package `pitypesafe`) |
17
+ | `src/client.ts` (`createTypeSafe`, budget, ledger, model mapping, backend transport) | `client.go` (`New`, `Evaluate`, `EvaluateRaw`, `ListModels`, `backendDoer`); the SDK calls are the shared client's |
18
+ | `src/backends.ts` | `backends.go` (+ the own-model backend, `hostmodel/`) |
19
+ | `src/schema.ts` (typebox schema, admission) | `schema.go`, `evaluation_schema.json` (the schema TypeBox serializes to, generated from the original), `json.go` (ordered JSON) |
20
+ | `src/errors.ts` | `errors.go` |
21
+ | `src/credentials.ts`, `src/auth.ts`, `src/usage.ts` | `credentials.go`, `auth.go`, `usage.go` |
22
+ | `src/batch.ts`, `src/ask.ts`, `src/calibrate.ts` | `batch.go`, `ask.go`, `calibrate.go` |
23
+ | `src/login.ts`, `src/key-prompt.ts`, `src/ui.ts` | `ui/login.go`, `ui/keyprompt.go` |
24
+ | `src/extension.ts` | `extensions/pi-typesafe/{extension,tool,command,format}.go` |
25
+ | `extensions/index.js`, `examples/decision-extension.ts` | the `pi.extensions` entry in `package.json`; the example is not ported (its content is in the README) |
26
+ | `tests/*.test.ts` | Go twins: `pi-typesafe-api/*_test.go`, `pi-typesafe-api/ui/ui_test.go`, `extensions/pi-typesafe/extension_test.go` |
27
+
28
+ ## Twins and scenarios
29
+
30
+ - **134 exact twins, 0 skipped** (`pigeq twins check`: 111 in `pi-typesafe-api`, 7 in `pi-typesafe-api/ui`, 16 in the extension).
31
+ Sub-cases with no Go counterpart are named inside their twin: JavaScript getters, `Date`, `BigInt`, `NaN` options, `null` (Go nil is
32
+ the default), a value of the wrong type in a struct field (covered through map-form endpoints).
33
+ - **Layer 2: 12 scenarios** (`port/scenarios`), golden traces recorded from the original under Pi 0.87.1 and each also identical under
34
+ PiG's Node runtime; the Go port reproduces every one (`pigeq check`: 14/14 with the gap and exec pseudo-scenarios). The scenarios
35
+ cover what needs no network: status, setup, login refusal, logout, consent accepted and declined, the disabled tool called by a
36
+ model, the playground's three no-send paths, and actions followed by extra words (`trailing-words`, added by the review). The harness reserves `PI_*` variables, so `PI_TYPESAFE_ENABLED` scenarios
37
+ cannot be scripted; those (headless opt-in, callouts) are layer-1 cases with the fake host. **Network paths** (a successful
38
+ evaluation, HTTP errors, model lists) run against a fake TypeSafe HTTP server in Go; the original cannot be pointed at one (it
39
+ deliberately ignores `TYPESAFE_BASE_URL`), so those are proven by the twins, not by traces.
40
+ - **Mutations** (`port/mutations.json`, `pigeq mutate --unit`): see Results.
41
+
42
+ ## Deliberate differences (each named, none silent)
43
+
44
+ 1. **PiG's directories.** State lives in `<PiG agent dir>/pi-typesafe/` (`PIG_CODING_AGENT_DIR`, else `$PIG_HOME/agent`, else `~/.pig/agent`;
45
+ Pi's `PI_CODING_AGENT_DIR`/`~/.pi/agent` with `PIG_USE_PI_DIRS=1`) instead of Pi's fixed directory (PiG divergence D2). Twin
46
+ "credentials live under Pi's agent directory" asserts PiG's directory.
47
+ 2. **Own-model backend** (owner requirement): `ownmodel` in the registry, `PI_TYPESAFE_BACKEND`, and `/typesafe backend [typesafe|ownmodel]`
48
+ (completed, not in the usage text). Unknown-backend text therefore lists `ownmodel`. Disclosure and consent follow the destination.
49
+ 3. **Zero values mean "default"** in `Options` (timeout, model, maxRequests, ...); negatives are rejected. The original's `timeoutMs: 0`, `NaN`
50
+ and `model: ""` rejections have no Go counterpart.
51
+ 4. **Key order in tool arguments.** PiG's Go SDK decodes tool arguments into maps, so question ids and Choice options arrive sorted, where JavaScript
52
+ keeps insertion order. Typed (`typesafe.SystemOneRequest`) and JSON-text (`ParseJSON`, the playground) paths keep order end to end.
53
+ The tool's `details` carry an extra `order` array so the renderer shows answers in question order. Reported as an SDK gap.
54
+ 5. **Validation wording.** Request-level failures (missing properties, counts, non-objects) use TypeBox's words (`testdata/typebox-messages.json`
55
+ compares them with the original); deep union-branch failures name the offending path with different words. The host validates tool
56
+ arguments against the same schema first, so the difference is reachable only from the playground and the library.
57
+ 6. **Model list** entries are read leniently as in the original; the Go client's typed decoder is bypassed (`ListRaw`).
58
+ 7. **Answer order** in `Evaluation` comes from the request's questions (`Order`), since answers are a map.
59
+ 8. **Scheduling.** Two requests started together race for the last allowed request; JavaScript ran the first to its cap check first. The batch twin "a daily cap stops the batch with the cap named" therefore asserts that exactly one of the two is refused, naming the cap.
60
+ 9. **Consent across a concurrent backend switch** (review). The original runs on one thread, so its consent check and its send cannot be
61
+ separated; here a `/typesafe backend` command can run while a tool call or the `/typesafe test` dialog is in progress. A switch
62
+ counts as a new destination: a call or dialog admitted before it stops with "The judgment backend changed after this request was
63
+ admitted; nothing was sent." (`clientFor`, `review_test.go`). A call already sending is not cancelled, as with `/typesafe disable`.
64
+ 10. **Loopback http: hosts** (review). The 127.0.0.0/8 rule also requires the host to parse as an IP address. WHATWG `URL` rejects
65
+ `127.999.0.1`, and Go's `url.Parse` accepts it as a name that DNS would resolve. The Go rule is stricter than the original's for forms
66
+ WHATWG normalizes (`127.1`, `0x7f.0.0.1`, `127.0.0.01`): they are refused, not rewritten.
67
+ 11. **Own-model errors** (review, PiG-only backend). Setup failures (no model selected, no Context) are configuration errors, so their
68
+ reason reaches the operator, and the callout names the own-model backend instead of a key.
69
+
70
+ SDK gap check (`pigeq gaps`): no blocking gaps. PARTIAL stand-ins: entry renderer, tool call/result renderers and prompt snippet render
71
+ lines instead of pi-tui Components (host limit shared by every Go extension).
72
+
73
+ ## Findings about the hosts and the harness
74
+
75
+ 1. A Package with an extension nested under a library root (`go.mod` at the Package root) breaks PiG's packed runner (it derives the
76
+ import path from the parent module) and `pigeq mutate` (the parent is copied as a sibling). Split the library into its own Package.
77
+ 2. `pigeq env`, `pigeq mutate --unit` and `scripts/go-modules.mjs` wrote a go.work in which the SDK is a `replace`; a library module that
78
+ requires another workspace module at `v0.0.0` then fails with "unknown revision". Fixed here (versioned replace for every used module,
79
+ test first) and reported to the porter driver.
80
+ 3. The Go SDK has no raw access to tool arguments (see difference 4).
81
+
82
+ ## Results (this revision)
83
+
84
+ - `go test -race` in `pi-typesafe-api` (root, `ui`) and the extension: pass. `GOOS=windows|darwin|linux go vet`: see the report.
85
+ - `pigeq check` against the Pi-recorded goldens: 12 of 12 scenarios plus `port-gaps` and `exec-coverage`.
86
+ - **Mutations (extension)**: `pigeq mutate --unit`, 50 mutants (4 added by the review), **50 killed** (33 by the fake-host tests alone; the scenarios kill the rest or share the kill).
87
+ The first run left 13 unproven; the added `branches_test.go` cases (callout channel and level, 403, logout's auth record, unusable stored
88
+ key, consent titles, entries, renderers, tool content) killed them, and three mutants that did not compile were rewritten.
89
+ - **Mutations (library)**: `port/library-mutations.py`, 22 applied mutants over client, backends, credentials, auth, usage, schema, errors, batch,
90
+ calibrate and ask: all killed after `gaps_test.go` was added, except mutant 5, which is equivalent (a model containing `/` cannot match either
91
+ OpenRouter rewrite rule) and was dropped.
92
+
93
+ ## Re-verified on PiG 0.4.1 (porter-verify)
94
+
95
+ The golden traces were recorded again from the original under Pi 1.0.1 (host check: identical under PiG's Node runtime), with PiG 0.4.1 content (`5f948f86a`, `pig --version`
96
+ `0.3.1+1.0.1`) and normalizer v3, because Pi 1.0.x changed the trace format: a `prompt` response now carries
97
+ `data.disposition` (Pi 0.99.0, #9098). An event-by-event diff against the previous traces shows no other
98
+ difference, and `pigeq check` passes on the Go port. Details: `docs/plan/progress/porter-verify.md`.
@@ -0,0 +1,3 @@
1
+ {
2
+ "exec:pi": "scripts/dev-pi.mjs is the upstream repository's development launcher: it is not in package.json `files` and Pi never loads it; the extension starts no process"
3
+ }
@@ -0,0 +1,11 @@
1
+ {"kind":"header","scenario":"enable-confirm","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-enable","ch":"ui","data":{"message":"Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.","method":"confirm","title":"Enable TypeSafe for this session?"}}
3
+ {"step":"01-enable","ch":"ui","data":{"message":"TypeSafe enabled. Up to 20 attempts in this session; /typesafe disable stops future agent calls.","method":"notify","notifyType":"info"}}
4
+ {"step":"01-enable","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
5
+ {"step":"02-status","ch":"ui","data":{"message":"TypeSafe: enabled. TypeSafe key: TYPESAFE_API_KEY (not verified yet — the first request proves it). Session 0/20 attempts; no client yet in this session. Model: jev-latest. Session limits reset on session start/reload; daily counters persist and caps come from client options or PI_TYPESAFE_MAX_* environment variables. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.","method":"notify","notifyType":"info"}}
6
+ {"step":"02-status","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
7
+ {"step":"03-disable","ch":"ui","data":{"message":"TypeSafe disabled for future agent calls. In-flight requests are not cancelled.","method":"notify","notifyType":"info"}}
8
+ {"step":"03-disable","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
9
+ {"step":"04-status-after","ch":"ui","data":{"message":"TypeSafe: disabled. TypeSafe key: TYPESAFE_API_KEY (not verified yet — the first request proves it). Session 0/20 attempts; no client yet in this session. Model: jev-latest. Session limits reset on session start/reload; daily counters persist and caps come from client options or PI_TYPESAFE_MAX_* environment variables. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.","method":"notify","notifyType":"info"}}
10
+ {"step":"04-status-after","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
11
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,20 @@
1
+ {"kind":"header","scenario":"enable-decline","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-enable","ch":"ui","data":{"message":"Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.","method":"confirm","title":"Enable TypeSafe for this session?"}}
3
+ {"step":"01-enable","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
4
+ {"step":"02-call","ch":"response","data":{"command":"prompt","data":{"disposition":"started"},"success":true}}
5
+ {"step":"02-call","ch":"host","data":{"type":"agent_start"}}
6
+ {"step":"02-call","ch":"host","data":{"type":"turn_start"}}
7
+ {"step":"02-call","ch":"host","data":{"message":{"content":"","role":"system"},"type":"message_end"}}
8
+ {"step":"02-call","ch":"host","data":{"message":{"content":[{"text":"judge it","type":"text"}],"role":"user"},"type":"message_end"}}
9
+ {"step":"02-call","ch":"llm","data":{"messages":[{"role":"user","text":"judge it"}],"system":{"inserted":"- typesafe_evaluate: Ask batched structured questions with TypeSafe (external service; operator opt-in required)\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n</tools>\n\n<rules>\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- You can inspect PI_* environment variables for current model and session details.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites.\n- Request shape, all three question kinds in one call: {\"state\":{\"message\":\"I was charged twice for my subscription. Please help today.\"},\"questions\":{\"category\":{\"type\":\"choice\",\"instructions\":\"Which team should handle this message?\",\"criteria\":{\"billing\":\"Charges and payments\",\"technical\":\"Software failures\",\"other\":\"None of these\"}},\"urgent\":{\"type\":\"noul\",\"instructions\":\"Does the sender request help today?\"},\"frustration\":{\"type\":\"score\",\"instructions\":\"How frustrated does the sender sound?\",\"criteria\":[\"Neutral request\",\"Frustrated but civil\",\"Angry or threatening\"]}}}\n- Use typesafe_evaluate only for requested semantic judgments, not calculations or exact lookups; send only the relevant permitted data.\n- Batch independent typesafe_evaluate questions over the same state; use code or explicit permission rules for actions, never confidence as authorization.\n- When typesafe_evaluate judges several items, give each item a named state field and ask one question per item per dimension, naming the field in the instructions; one question over many items returns an unusable blend.\n- Report typesafe_evaluate answers as the model's judgments with their probabilities; do not replace them with your own guesses, and say when an answer is uncertain","removed":"\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n</tools>\n\n<rules>\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- You can inspect PI_* environment variables for current model and session details.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites"},"tools":[{"description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","name":"read","parameters":{"properties":{"limit":{"description":"Maximum number of lines to read","type":"number"},"offset":{"description":"Line number to start reading from (1-indexed)","type":"number"},"path":{"description":"Path to the file to read (relative or absolute)","type":"string"}},"required":["path"],"type":"object"}},{"description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","name":"bash","parameters":{"properties":{"command":{"description":"Shell command to execute","type":"string"},"timeout":{"description":"Timeout in seconds (optional, no default timeout)","type":"number"}},"required":["command"],"type":"object"}},{"description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","name":"edit","parameters":{"properties":{"edits":{"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.","items":{"properties":{"newText":{"description":"Replacement text for this targeted edit.","type":"string"},"oldText":{"description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.","type":"string"}},"required":["oldText","newText"],"type":"object"},"type":"array"},"path":{"description":"Path to the file to edit (relative or absolute)","type":"string"}},"required":["path","edits"],"type":"object"}},{"description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","name":"write","parameters":{"properties":{"content":{"description":"Content to write to the file","type":"string"},"path":{"description":"Path to the file to write (relative or absolute)","type":"string"}},"required":["path","content"],"type":"object"}},{"description":"Evaluate supplied state with independent Choice, Score, and Noul questions in one TypeSafe request. Each question judges the whole state, so when several items are involved, put each item in a named state field (e.g. `reports.r1`) and ask one question per item per dimension (e.g. `r1_owner`, `r2_owner`), naming the field in the instructions; never aggregate several items into one question. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization. Requires operator opt-in via /typesafe enable or PI_TYPESAFE_ENABLED=1. Limit: 32 questions, 64 KiB JSON, 20 attempts per session; no retries.","name":"typesafe_evaluate","parameters":{"additionalProperties":false,"properties":{"model":{"description":"Jev model id, e.g. jev-latest. Omit for the default.","maxLength":100,"minLength":1,"type":"string"},"questions":{"description":"Questions keyed by a short id, as an object map, not an array: { \"urgent\": { type: \"noul\", instructions: ... } }.","maxProperties":32,"minProperties":1,"patternProperties":{"^.*$":{"anyOf":[{"additionalProperties":false,"properties":{"criteria":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"false":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]},"true":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]}},"type":"object"}],"description":"Optional: what counts as yes and what counts as no, { true, false }."},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"noul","description":"Yes or no: the probability the instructions hold.","type":"string"}},"required":["type"],"type":"object"},{"additionalProperties":false,"properties":{"criteria":{"description":"The options as a map from label to when it applies: { billing: \"Charges and payments\", other: null }. 1–64 entries.","maxProperties":64,"minProperties":1,"patternProperties":{"^.*$":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]}},"type":"object"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"choice","description":"Pick one criteria label.","type":"string"}},"required":["type","criteria"],"type":"object"},{"additionalProperties":false,"properties":{"criteria":{"description":"Ordered rubric levels, lowest first: [\"neutral\", \"angry\"]. 2–32 levels.","items":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]},"maxItems":32,"minItems":2,"type":"array"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"score","description":"Rate against the ordered criteria levels.","type":"string"}},"required":["type","criteria"],"type":"object"}]}},"type":"object"},"state":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"What to judge: text, or an object whose fields the questions name."}},"required":["state","questions"],"type":"object"}}]}}
10
+ {"step":"02-call","ch":"host","data":{"message":{"content":[{"arguments":{"questions":{"yes":{"instructions":"?","type":"noul"}},"state":"s"},"name":"typesafe_evaluate","type":"toolCall"}],"role":"assistant","stopReason":"toolUse"},"type":"message_end"}}
11
+ {"step":"02-call","ch":"host","data":{"args":{"questions":{"yes":{"instructions":"?","type":"noul"}},"state":"s"},"toolName":"typesafe_evaluate","type":"tool_execution_start"}}
12
+ {"step":"02-call","ch":"host","data":{"isError":true,"result":{"content":[{"text":"TypeSafe is disabled. Ask the operator to run /typesafe enable; do not enable it by editing configuration or environment files.","type":"text"}],"details":{}},"toolName":"typesafe_evaluate","type":"tool_execution_end"}}
13
+ {"step":"02-call","ch":"host","data":{"message":{"content":[{"text":"TypeSafe is disabled. Ask the operator to run /typesafe enable; do not enable it by editing configuration or environment files.","type":"text"}],"details":{},"isError":true,"role":"toolResult","toolName":"typesafe_evaluate"},"type":"message_end"}}
14
+ {"step":"02-call","ch":"host","data":{"type":"turn_end"}}
15
+ {"step":"02-call","ch":"host","data":{"type":"turn_start"}}
16
+ {"step":"02-call","ch":"llm","data":{"messages":[{"role":"user","text":"judge it"},{"role":"assistant","text":"","toolCalls":[{"arguments":"{\"questions\":{\"yes\":{\"instructions\":\"?\",\"type\":\"noul\"}},\"state\":\"s\"}","name":"typesafe_evaluate"}]},{"role":"tool","text":"TypeSafe is disabled. Ask the operator to run /typesafe enable; do not enable it by editing configuration or environment files."}],"system":{"inserted":"- typesafe_evaluate: Ask batched structured questions with TypeSafe (external service; operator opt-in required)\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n</tools>\n\n<rules>\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- You can inspect PI_* environment variables for current model and session details.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites.\n- Request shape, all three question kinds in one call: {\"state\":{\"message\":\"I was charged twice for my subscription. Please help today.\"},\"questions\":{\"category\":{\"type\":\"choice\",\"instructions\":\"Which team should handle this message?\",\"criteria\":{\"billing\":\"Charges and payments\",\"technical\":\"Software failures\",\"other\":\"None of these\"}},\"urgent\":{\"type\":\"noul\",\"instructions\":\"Does the sender request help today?\"},\"frustration\":{\"type\":\"score\",\"instructions\":\"How frustrated does the sender sound?\",\"criteria\":[\"Neutral request\",\"Frustrated but civil\",\"Angry or threatening\"]}}}\n- Use typesafe_evaluate only for requested semantic judgments, not calculations or exact lookups; send only the relevant permitted data.\n- Batch independent typesafe_evaluate questions over the same state; use code or explicit permission rules for actions, never confidence as authorization.\n- When typesafe_evaluate judges several items, give each item a named state field and ask one question per item per dimension, naming the field in the instructions; one question over many items returns an unusable blend.\n- Report typesafe_evaluate answers as the model's judgments with their probabilities; do not replace them with your own guesses, and say when an answer is uncertain","removed":"\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n</tools>\n\n<rules>\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- You can inspect PI_* environment variables for current model and session details.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites"},"tools":[{"description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","name":"read","parameters":{"properties":{"limit":{"description":"Maximum number of lines to read","type":"number"},"offset":{"description":"Line number to start reading from (1-indexed)","type":"number"},"path":{"description":"Path to the file to read (relative or absolute)","type":"string"}},"required":["path"],"type":"object"}},{"description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","name":"bash","parameters":{"properties":{"command":{"description":"Shell command to execute","type":"string"},"timeout":{"description":"Timeout in seconds (optional, no default timeout)","type":"number"}},"required":["command"],"type":"object"}},{"description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","name":"edit","parameters":{"properties":{"edits":{"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.","items":{"properties":{"newText":{"description":"Replacement text for this targeted edit.","type":"string"},"oldText":{"description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.","type":"string"}},"required":["oldText","newText"],"type":"object"},"type":"array"},"path":{"description":"Path to the file to edit (relative or absolute)","type":"string"}},"required":["path","edits"],"type":"object"}},{"description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","name":"write","parameters":{"properties":{"content":{"description":"Content to write to the file","type":"string"},"path":{"description":"Path to the file to write (relative or absolute)","type":"string"}},"required":["path","content"],"type":"object"}},{"description":"Evaluate supplied state with independent Choice, Score, and Noul questions in one TypeSafe request. Each question judges the whole state, so when several items are involved, put each item in a named state field (e.g. `reports.r1`) and ask one question per item per dimension (e.g. `r1_owner`, `r2_owner`), naming the field in the instructions; never aggregate several items into one question. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization. Requires operator opt-in via /typesafe enable or PI_TYPESAFE_ENABLED=1. Limit: 32 questions, 64 KiB JSON, 20 attempts per session; no retries.","name":"typesafe_evaluate","parameters":{"additionalProperties":false,"properties":{"model":{"description":"Jev model id, e.g. jev-latest. Omit for the default.","maxLength":100,"minLength":1,"type":"string"},"questions":{"description":"Questions keyed by a short id, as an object map, not an array: { \"urgent\": { type: \"noul\", instructions: ... } }.","maxProperties":32,"minProperties":1,"patternProperties":{"^.*$":{"anyOf":[{"additionalProperties":false,"properties":{"criteria":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"false":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]},"true":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]}},"type":"object"}],"description":"Optional: what counts as yes and what counts as no, { true, false }."},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"noul","description":"Yes or no: the probability the instructions hold.","type":"string"}},"required":["type"],"type":"object"},{"additionalProperties":false,"properties":{"criteria":{"description":"The options as a map from label to when it applies: { billing: \"Charges and payments\", other: null }. 1–64 entries.","maxProperties":64,"minProperties":1,"patternProperties":{"^.*$":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]}},"type":"object"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"choice","description":"Pick one criteria label.","type":"string"}},"required":["type","criteria"],"type":"object"},{"additionalProperties":false,"properties":{"criteria":{"description":"Ordered rubric levels, lowest first: [\"neutral\", \"angry\"]. 2–32 levels.","items":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]},"maxItems":32,"minItems":2,"type":"array"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"score","description":"Rate against the ordered criteria levels.","type":"string"}},"required":["type","criteria"],"type":"object"}]}},"type":"object"},"state":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"What to judge: text, or an object whose fields the questions name."}},"required":["state","questions"],"type":"object"}}]}}
17
+ {"step":"02-call","ch":"host","data":{"message":{"content":[{"text":"done","type":"text"}],"role":"assistant","stopReason":"stop"},"type":"message_end"}}
18
+ {"step":"02-call","ch":"host","data":{"type":"turn_end"}}
19
+ {"step":"02-call","ch":"host","data":{"type":"agent_end"}}
20
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,4 @@
1
+ {"kind":"header","scenario":"enable-missing-key","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-enable","ch":"ui","data":{"message":"Run /typesafe login first: no API key is configured.","method":"notify","notifyType":"warning"}}
3
+ {"step":"01-enable","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
4
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,4 @@
1
+ {"kind":"header","scenario":"login-shadow","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-login","ch":"ui","data":{"message":"TYPESAFE_API_KEY is set in the environment and takes precedence over a stored key. Unset it before using /typesafe login.","method":"notify","notifyType":"warning"}}
3
+ {"step":"01-login","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
4
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,6 @@
1
+ {"kind":"header","scenario":"logout-env-key","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-logout","ch":"ui","data":{"message":"No stored key to remove. TYPESAFE_API_KEY is still set in the environment.","method":"notify","notifyType":"info"}}
3
+ {"step":"01-logout","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
4
+ {"step":"02-status","ch":"ui","data":{"message":"TypeSafe: disabled. TypeSafe key: TYPESAFE_API_KEY (not verified yet — the first request proves it). Session 0/20 attempts; no client yet in this session. Model: jev-latest. Session limits reset on session start/reload; daily counters persist and caps come from client options or PI_TYPESAFE_MAX_* environment variables. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.","method":"notify","notifyType":"info"}}
5
+ {"step":"02-status","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
6
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,4 @@
1
+ {"kind":"header","scenario":"playground-cancel","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-playground","ch":"ui","data":{"method":"editor","prefill":"{\n \"state\": {\n \"message\": \"I was charged twice for my subscription. Please help today.\"\n },\n \"questions\": {\n \"category\": {\n \"type\": \"choice\",\n \"instructions\": \"Which team should handle this message?\",\n \"criteria\": {\n \"billing\": \"Charges and payments\",\n \"technical\": \"Software failures\",\n \"other\": \"None of these\"\n }\n },\n \"urgent\": {\n \"type\": \"noul\",\n \"instructions\": \"Does the sender request help today?\"\n },\n \"frustration\": {\n \"type\": \"score\",\n \"instructions\": \"How frustrated does the sender sound?\",\n \"criteria\": [\n \"Neutral request\",\n \"Frustrated but civil\",\n \"Angry or threatening\"\n ]\n }\n }\n}","title":"TypeSafe request JSON · edit state and questions"}}
3
+ {"step":"01-playground","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
4
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,5 @@
1
+ {"kind":"header","scenario":"playground-invalid-json","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-playground","ch":"ui","data":{"method":"editor","prefill":"{\n \"state\": {\n \"message\": \"I was charged twice for my subscription. Please help today.\"\n },\n \"questions\": {\n \"category\": {\n \"type\": \"choice\",\n \"instructions\": \"Which team should handle this message?\",\n \"criteria\": {\n \"billing\": \"Charges and payments\",\n \"technical\": \"Software failures\",\n \"other\": \"None of these\"\n }\n },\n \"urgent\": {\n \"type\": \"noul\",\n \"instructions\": \"Does the sender request help today?\"\n },\n \"frustration\": {\n \"type\": \"score\",\n \"instructions\": \"How frustrated does the sender sound?\",\n \"criteria\": [\n \"Neutral request\",\n \"Frustrated but civil\",\n \"Angry or threatening\"\n ]\n }\n }\n}","title":"TypeSafe request JSON · edit state and questions"}}
3
+ {"step":"01-playground","ch":"ui","data":{"message":"Invalid JSON. Keep quoted strings on one line; nothing was sent.","method":"notify","notifyType":"error"}}
4
+ {"step":"01-playground","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
5
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,5 @@
1
+ {"kind":"header","scenario":"playground-invalid-questions","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-playground","ch":"ui","data":{"method":"editor","prefill":"{\n \"state\": {\n \"message\": \"I was charged twice for my subscription. Please help today.\"\n },\n \"questions\": {\n \"category\": {\n \"type\": \"choice\",\n \"instructions\": \"Which team should handle this message?\",\n \"criteria\": {\n \"billing\": \"Charges and payments\",\n \"technical\": \"Software failures\",\n \"other\": \"None of these\"\n }\n },\n \"urgent\": {\n \"type\": \"noul\",\n \"instructions\": \"Does the sender request help today?\"\n },\n \"frustration\": {\n \"type\": \"score\",\n \"instructions\": \"How frustrated does the sender sound?\",\n \"criteria\": [\n \"Neutral request\",\n \"Frustrated but civil\",\n \"Angry or threatening\"\n ]\n }\n }\n}","title":"TypeSafe request JSON · edit state and questions"}}
3
+ {"step":"01-playground","ch":"ui","data":{"message":"Invalid evaluation request at questions: must not have fewer than 1 properties. Expected { state, questions: { <id>: { type: \"choice\", instructions, criteria: { label: description|null } } | { type: \"score\", instructions, criteria: [level0, level1, ...] } | { type: \"noul\", instructions } } }; 1–32 questions, Choice 1–64 options, Score 2–32 levels.","method":"notify","notifyType":"error"}}
4
+ {"step":"01-playground","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
5
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,6 @@
1
+ {"kind":"header","scenario":"status-env-key","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-status","ch":"ui","data":{"message":"TypeSafe: disabled. TypeSafe key: TYPESAFE_API_KEY (not verified yet — the first request proves it). Session 0/20 attempts; no client yet in this session. Model: jev-latest. Session limits reset on session start/reload; daily counters persist and caps come from client options or PI_TYPESAFE_MAX_* environment variables. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.","method":"notify","notifyType":"info"}}
3
+ {"step":"01-status","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
4
+ {"step":"02-setup","ch":"ui","data":{"message":"Key configured via TYPESAFE_API_KEY. Run /typesafe test for one sample request or /typesafe enable to allow agent tool calls.","method":"notify","notifyType":"info"}}
5
+ {"step":"02-setup","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
6
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,6 @@
1
+ {"kind":"header","scenario":"status-no-key","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-status","ch":"ui","data":{"message":"TypeSafe: disabled. TypeSafe key: missing — every Jev judgment is skipped until a key is configured (/typesafe login or TYPESAFE_API_KEY). Session 0/20 attempts; no client yet in this session. Model: jev-latest. Session limits reset on session start/reload; daily counters persist and caps come from client options or PI_TYPESAFE_MAX_* environment variables. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.","method":"notify","notifyType":"info"}}
3
+ {"step":"01-status","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
4
+ {"step":"02-usage","ch":"ui","data":{"message":"Usage: /typesafe login | logout | setup | status | enable | disable | test | playground","method":"notify","notifyType":"warning"}}
5
+ {"step":"02-usage","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
6
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,18 @@
1
+ {"kind":"header","scenario":"tool-disabled","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-call","ch":"response","data":{"command":"prompt","data":{"disposition":"started"},"success":true}}
3
+ {"step":"01-call","ch":"host","data":{"type":"agent_start"}}
4
+ {"step":"01-call","ch":"host","data":{"type":"turn_start"}}
5
+ {"step":"01-call","ch":"host","data":{"message":{"content":"","role":"system"},"type":"message_end"}}
6
+ {"step":"01-call","ch":"host","data":{"message":{"content":[{"text":"judge it","type":"text"}],"role":"user"},"type":"message_end"}}
7
+ {"step":"01-call","ch":"llm","data":{"messages":[{"role":"user","text":"judge it"}],"system":{"inserted":"- typesafe_evaluate: Ask batched structured questions with TypeSafe (external service; operator opt-in required)\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n</tools>\n\n<rules>\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- You can inspect PI_* environment variables for current model and session details.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites.\n- Request shape, all three question kinds in one call: {\"state\":{\"message\":\"I was charged twice for my subscription. Please help today.\"},\"questions\":{\"category\":{\"type\":\"choice\",\"instructions\":\"Which team should handle this message?\",\"criteria\":{\"billing\":\"Charges and payments\",\"technical\":\"Software failures\",\"other\":\"None of these\"}},\"urgent\":{\"type\":\"noul\",\"instructions\":\"Does the sender request help today?\"},\"frustration\":{\"type\":\"score\",\"instructions\":\"How frustrated does the sender sound?\",\"criteria\":[\"Neutral request\",\"Frustrated but civil\",\"Angry or threatening\"]}}}\n- Use typesafe_evaluate only for requested semantic judgments, not calculations or exact lookups; send only the relevant permitted data.\n- Batch independent typesafe_evaluate questions over the same state; use code or explicit permission rules for actions, never confidence as authorization.\n- When typesafe_evaluate judges several items, give each item a named state field and ask one question per item per dimension, naming the field in the instructions; one question over many items returns an unusable blend.\n- Report typesafe_evaluate answers as the model's judgments with their probabilities; do not replace them with your own guesses, and say when an answer is uncertain","removed":"\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n</tools>\n\n<rules>\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- You can inspect PI_* environment variables for current model and session details.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites"},"tools":[{"description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","name":"read","parameters":{"properties":{"limit":{"description":"Maximum number of lines to read","type":"number"},"offset":{"description":"Line number to start reading from (1-indexed)","type":"number"},"path":{"description":"Path to the file to read (relative or absolute)","type":"string"}},"required":["path"],"type":"object"}},{"description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","name":"bash","parameters":{"properties":{"command":{"description":"Shell command to execute","type":"string"},"timeout":{"description":"Timeout in seconds (optional, no default timeout)","type":"number"}},"required":["command"],"type":"object"}},{"description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","name":"edit","parameters":{"properties":{"edits":{"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.","items":{"properties":{"newText":{"description":"Replacement text for this targeted edit.","type":"string"},"oldText":{"description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.","type":"string"}},"required":["oldText","newText"],"type":"object"},"type":"array"},"path":{"description":"Path to the file to edit (relative or absolute)","type":"string"}},"required":["path","edits"],"type":"object"}},{"description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","name":"write","parameters":{"properties":{"content":{"description":"Content to write to the file","type":"string"},"path":{"description":"Path to the file to write (relative or absolute)","type":"string"}},"required":["path","content"],"type":"object"}},{"description":"Evaluate supplied state with independent Choice, Score, and Noul questions in one TypeSafe request. Each question judges the whole state, so when several items are involved, put each item in a named state field (e.g. `reports.r1`) and ask one question per item per dimension (e.g. `r1_owner`, `r2_owner`), naming the field in the instructions; never aggregate several items into one question. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization. Requires operator opt-in via /typesafe enable or PI_TYPESAFE_ENABLED=1. Limit: 32 questions, 64 KiB JSON, 20 attempts per session; no retries.","name":"typesafe_evaluate","parameters":{"additionalProperties":false,"properties":{"model":{"description":"Jev model id, e.g. jev-latest. Omit for the default.","maxLength":100,"minLength":1,"type":"string"},"questions":{"description":"Questions keyed by a short id, as an object map, not an array: { \"urgent\": { type: \"noul\", instructions: ... } }.","maxProperties":32,"minProperties":1,"patternProperties":{"^.*$":{"anyOf":[{"additionalProperties":false,"properties":{"criteria":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"false":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]},"true":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]}},"type":"object"}],"description":"Optional: what counts as yes and what counts as no, { true, false }."},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"noul","description":"Yes or no: the probability the instructions hold.","type":"string"}},"required":["type"],"type":"object"},{"additionalProperties":false,"properties":{"criteria":{"description":"The options as a map from label to when it applies: { billing: \"Charges and payments\", other: null }. 1–64 entries.","maxProperties":64,"minProperties":1,"patternProperties":{"^.*$":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]}},"type":"object"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"choice","description":"Pick one criteria label.","type":"string"}},"required":["type","criteria"],"type":"object"},{"additionalProperties":false,"properties":{"criteria":{"description":"Ordered rubric levels, lowest first: [\"neutral\", \"angry\"]. 2–32 levels.","items":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]},"maxItems":32,"minItems":2,"type":"array"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"score","description":"Rate against the ordered criteria levels.","type":"string"}},"required":["type","criteria"],"type":"object"}]}},"type":"object"},"state":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"What to judge: text, or an object whose fields the questions name."}},"required":["state","questions"],"type":"object"}}]}}
8
+ {"step":"01-call","ch":"host","data":{"message":{"content":[{"arguments":{"questions":{"yes":{"instructions":"?","type":"noul"}},"state":"s"},"name":"typesafe_evaluate","type":"toolCall"}],"role":"assistant","stopReason":"toolUse"},"type":"message_end"}}
9
+ {"step":"01-call","ch":"host","data":{"args":{"questions":{"yes":{"instructions":"?","type":"noul"}},"state":"s"},"toolName":"typesafe_evaluate","type":"tool_execution_start"}}
10
+ {"step":"01-call","ch":"host","data":{"isError":true,"result":{"content":[{"text":"TypeSafe is disabled. Ask the operator to run /typesafe enable; do not enable it by editing configuration or environment files.","type":"text"}],"details":{}},"toolName":"typesafe_evaluate","type":"tool_execution_end"}}
11
+ {"step":"01-call","ch":"host","data":{"message":{"content":[{"text":"TypeSafe is disabled. Ask the operator to run /typesafe enable; do not enable it by editing configuration or environment files.","type":"text"}],"details":{},"isError":true,"role":"toolResult","toolName":"typesafe_evaluate"},"type":"message_end"}}
12
+ {"step":"01-call","ch":"host","data":{"type":"turn_end"}}
13
+ {"step":"01-call","ch":"host","data":{"type":"turn_start"}}
14
+ {"step":"01-call","ch":"llm","data":{"messages":[{"role":"user","text":"judge it"},{"role":"assistant","text":"","toolCalls":[{"arguments":"{\"questions\":{\"yes\":{\"instructions\":\"?\",\"type\":\"noul\"}},\"state\":\"s\"}","name":"typesafe_evaluate"}]},{"role":"tool","text":"TypeSafe is disabled. Ask the operator to run /typesafe enable; do not enable it by editing configuration or environment files."}],"system":{"inserted":"- typesafe_evaluate: Ask batched structured questions with TypeSafe (external service; operator opt-in required)\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n</tools>\n\n<rules>\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- You can inspect PI_* environment variables for current model and session details.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites.\n- Request shape, all three question kinds in one call: {\"state\":{\"message\":\"I was charged twice for my subscription. Please help today.\"},\"questions\":{\"category\":{\"type\":\"choice\",\"instructions\":\"Which team should handle this message?\",\"criteria\":{\"billing\":\"Charges and payments\",\"technical\":\"Software failures\",\"other\":\"None of these\"}},\"urgent\":{\"type\":\"noul\",\"instructions\":\"Does the sender request help today?\"},\"frustration\":{\"type\":\"score\",\"instructions\":\"How frustrated does the sender sound?\",\"criteria\":[\"Neutral request\",\"Frustrated but civil\",\"Angry or threatening\"]}}}\n- Use typesafe_evaluate only for requested semantic judgments, not calculations or exact lookups; send only the relevant permitted data.\n- Batch independent typesafe_evaluate questions over the same state; use code or explicit permission rules for actions, never confidence as authorization.\n- When typesafe_evaluate judges several items, give each item a named state field and ask one question per item per dimension, naming the field in the instructions; one question over many items returns an unusable blend.\n- Report typesafe_evaluate answers as the model's judgments with their probabilities; do not replace them with your own guesses, and say when an answer is uncertain","removed":"\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n</tools>\n\n<rules>\n- Use bash for file operations like ls, rg, find\n- Use read to examine files instead of cat or sed.\n- You can inspect PI_* environment variables for current model and session details.\n- Use edit for precise changes (edits[].oldText must match exactly)\n- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\n- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\n- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\n- Use write only for new files or complete rewrites"},"tools":[{"description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","name":"read","parameters":{"properties":{"limit":{"description":"Maximum number of lines to read","type":"number"},"offset":{"description":"Line number to start reading from (1-indexed)","type":"number"},"path":{"description":"Path to the file to read (relative or absolute)","type":"string"}},"required":["path"],"type":"object"}},{"description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","name":"bash","parameters":{"properties":{"command":{"description":"Shell command to execute","type":"string"},"timeout":{"description":"Timeout in seconds (optional, no default timeout)","type":"number"}},"required":["command"],"type":"object"}},{"description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","name":"edit","parameters":{"properties":{"edits":{"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.","items":{"properties":{"newText":{"description":"Replacement text for this targeted edit.","type":"string"},"oldText":{"description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.","type":"string"}},"required":["oldText","newText"],"type":"object"},"type":"array"},"path":{"description":"Path to the file to edit (relative or absolute)","type":"string"}},"required":["path","edits"],"type":"object"}},{"description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","name":"write","parameters":{"properties":{"content":{"description":"Content to write to the file","type":"string"},"path":{"description":"Path to the file to write (relative or absolute)","type":"string"}},"required":["path","content"],"type":"object"}},{"description":"Evaluate supplied state with independent Choice, Score, and Noul questions in one TypeSafe request. Each question judges the whole state, so when several items are involved, put each item in a named state field (e.g. `reports.r1`) and ask one question per item per dimension (e.g. `r1_owner`, `r2_owner`), naming the field in the instructions; never aggregate several items into one question. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization. Requires operator opt-in via /typesafe enable or PI_TYPESAFE_ENABLED=1. Limit: 32 questions, 64 KiB JSON, 20 attempts per session; no retries.","name":"typesafe_evaluate","parameters":{"additionalProperties":false,"properties":{"model":{"description":"Jev model id, e.g. jev-latest. Omit for the default.","maxLength":100,"minLength":1,"type":"string"},"questions":{"description":"Questions keyed by a short id, as an object map, not an array: { \"urgent\": { type: \"noul\", instructions: ... } }.","maxProperties":32,"minProperties":1,"patternProperties":{"^.*$":{"anyOf":[{"additionalProperties":false,"properties":{"criteria":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"false":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]},"true":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]}},"type":"object"}],"description":"Optional: what counts as yes and what counts as no, { true, false }."},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"noul","description":"Yes or no: the probability the instructions hold.","type":"string"}},"required":["type"],"type":"object"},{"additionalProperties":false,"properties":{"criteria":{"description":"The options as a map from label to when it applies: { billing: \"Charges and payments\", other: null }. 1–64 entries.","maxProperties":64,"minProperties":1,"patternProperties":{"^.*$":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]}},"type":"object"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"choice","description":"Pick one criteria label.","type":"string"}},"required":["type","criteria"],"type":"object"},{"additionalProperties":false,"properties":{"criteria":{"description":"Ordered rubric levels, lowest first: [\"neutral\", \"angry\"]. 2–32 levels.","items":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}]},"maxItems":32,"minItems":2,"type":"array"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"One judgment about the whole state, phrased as a question or a statement."},"type":{"const":"score","description":"Rate against the ordered criteria levels.","type":"string"}},"required":["type","criteria"],"type":"object"}]}},"type":"object"},"state":{"anyOf":[{"type":"string"},{"type":"null"},{"items":{},"type":"array"},{"patternProperties":{"^.*$":{}},"type":"object"}],"description":"What to judge: text, or an object whose fields the questions name."}},"required":["state","questions"],"type":"object"}}]}}
15
+ {"step":"01-call","ch":"host","data":{"message":{"content":[{"text":"done","type":"text"}],"role":"assistant","stopReason":"stop"},"type":"message_end"}}
16
+ {"step":"01-call","ch":"host","data":{"type":"turn_end"}}
17
+ {"step":"01-call","ch":"host","data":{"type":"agent_end"}}
18
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,10 @@
1
+ {"kind":"header","scenario":"trailing-words","lane":"pi-ts","host":"pi 1.0.1","extension":"sha256:2dbf2078e91fc7fface683f6ff536e10e8218b387b768a473d7d11f296272329","normalizer":"v3"}
2
+ {"step":"01-status-now","ch":"ui","data":{"message":"Usage: /typesafe login | logout | setup | status | enable | disable | test | playground","method":"notify","notifyType":"warning"}}
3
+ {"step":"01-status-now","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
4
+ {"step":"02-enable-yes","ch":"ui","data":{"message":"Usage: /typesafe login | logout | setup | status | enable | disable | test | playground","method":"notify","notifyType":"warning"}}
5
+ {"step":"02-enable-yes","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
6
+ {"step":"03-logout-please","ch":"ui","data":{"message":"Usage: /typesafe login | logout | setup | status | enable | disable | test | playground","method":"notify","notifyType":"warning"}}
7
+ {"step":"03-logout-please","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
8
+ {"step":"04-status","ch":"ui","data":{"message":"TypeSafe: disabled. TypeSafe key: TYPESAFE_API_KEY (not verified yet — the first request proves it). Session 0/20 attempts; no client yet in this session. Model: jev-latest. Session limits reset on session start/reload; daily counters persist and caps come from client options or PI_TYPESAFE_MAX_* environment variables. Submitted state and questions will be sent to api.typesafe.ai and may incur charges. Do not include secrets. The extension does not collect files or conversation history. Results are model judgments, not proof or authorization.","method":"notify","notifyType":"info"}}
9
+ {"step":"04-status","ch":"response","data":{"command":"prompt","data":{"disposition":"handled"},"success":true}}
10
+ {"step":"99-shutdown","ch":"exit","data":{"code":0}}
@@ -0,0 +1,44 @@
1
+ # Library mutation check: applies each (file, find, replace) to a copy of components/pi-typesafe-api and runs its tests; expects every mutant killed.
2
+ # Needs a go.work at /tmp/pts/go.work that uses the SDK, pi-typesafe-api and typesafe (see port/PORT.md). Mutant 5 is equivalent (removed); 13 and 18 no longer match the source.
3
+ import subprocess,shutil,os,sys
4
+ HERE=os.path.dirname(os.path.abspath(__file__))
5
+ API=os.path.normpath(os.path.join(HERE,'..','..','pi-typesafe-api'))
6
+ WORK=os.environ.get('LIBMUT_GOWORK','/tmp/pts/go.work') # a go.work that uses the SDK dir, pi-typesafe-api and typesafe
7
+ M=[
8
+ ("client.go","if c.usage.RequestsStarted >= c.maxRequests {\n\t\tc.mu.Unlock()","if c.usage.RequestsStarted > c.maxRequests {\n\t\tc.mu.Unlock()"),
9
+ ("client.go","c.usage.RequestsStarted++\n\tc.ledger.RecordStart()","c.ledger.RecordStart()"),
10
+ ("client.go","if ctx.Err() != nil {\n\t\treturn nil, newError(CodeAborted","if false {\n\t\treturn nil, newError(CodeAborted"),
11
+ ("client.go","if c.backend.ModelsVerifyKey {","if true {"),
12
+ ("client.go","transport = backendDoer{inner: opts.HTTPClient, backend: backend}","_ = backend"),
13
+ ("backends.go","if strings.Contains(model, \"/\") || backend != BackendOpenRouter {","if backend != BackendOpenRouter {"),
14
+ ("backends.go","strings.EqualFold(keyEnv, typesafeKeyEnv)","keyEnv == typesafeKeyEnv"),
15
+ ("backends.go","return !b.Local && (b.KeyEnv == \"\" || b.KeyEnv == typesafeKeyEnv)","return !b.Local"),
16
+ ("credentials.go","info.Mode().Perm()&0o077 != 0","info.Mode().Perm()&0o007 != 0"),
17
+ ("credentials.go","len(key) >= 16","len(key) >= 4"),
18
+ ("auth.go","(f.Status == 401 || f.Status == 403)","(f.Status == 401)"),
19
+ ("auth.go","f.Code == CodeHTTP &&","true &&"),
20
+ ("usage.go","if c.limit > 0 && c.used >= c.limit {","if c.limit > 0 && c.used > c.limit {"),
21
+ ("usage.go","return min(a, b)\n\t}\n\treturn SpendCaps","return max(a, b)\n\t}\n\treturn SpendCaps"),
22
+ ("schema.go","if qs.Len() < 1 || qs.Len() > DefaultMaxQuestions {","if qs.Len() < 1 || qs.Len() > DefaultMaxQuestions+1 {"),
23
+ ("schema.go","if len(text) > maxInputBytes","if len(text) >= maxInputBytes"),
24
+ ("schema.go",'item.Set("criteria", labels)','_ = labels'),
25
+ ("errors.go",'advice += " Retry after "','advice += " Wait "'),
26
+ ("errors.go",'if openrouter','if false'),
27
+ ("batch.go","if ctx.Err() != nil {\n\t\t\t\tstopped = true","if false {\n\t\t\t\tstopped = true"),
28
+ ("batch.go","return ok && (ie.Code == CodeBudget || ie.Code == CodeAborted)","return ok && ie.Code == CodeBudget"),
29
+ ("calibrate.go","case p == n:\n\t\t\t\twins += 0.5","case p == n:\n\t\t\t\twins += 1"),
30
+ ("ask.go","timeout = DefaultAskTimeout","timeout = 0"),
31
+ ]
32
+ res=[]
33
+ for i,(f,a,b) in enumerate(M):
34
+ d=f'/tmp/libmut/{i}'
35
+ shutil.rmtree(d,ignore_errors=True); shutil.copytree(API,d,ignore=shutil.ignore_patterns('.git'))
36
+ p=os.path.join(d,f); t=open(p).read()
37
+ if t.count(a)!=1: print('BAD',i,f,t.count(a)); continue
38
+ open(p,'w').write(t.replace(a,b))
39
+ env=dict(os.environ);
40
+ work=open(WORK).read().replace(API,d)
41
+ open(d+'/go.work.tmp','w').write(work); env['GOWORK']=d+'/go.work.tmp'
42
+ r=subprocess.run(['go','test','-count=1','-timeout=120s','.'],cwd=d,env=env,capture_output=True,text=True)
43
+ status='KILLED' if r.returncode!=0 and ('FAIL' in r.stdout) else ('INVALID' if r.returncode!=0 else 'SURVIVED')
44
+ print(status,i,f,a[:40].replace('\n',' '))