@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.
- package/CREDITS.md +14 -0
- package/LICENSE +22 -0
- package/README.md +45 -0
- package/extensions/pi-typesafe/branches_test.go +185 -0
- package/extensions/pi-typesafe/command.go +319 -0
- package/extensions/pi-typesafe/export_test.go +9 -0
- package/extensions/pi-typesafe/extension.go +188 -0
- package/extensions/pi-typesafe/extension_test.go +321 -0
- package/extensions/pi-typesafe/fakehost_test.go +548 -0
- package/extensions/pi-typesafe/format.go +191 -0
- package/extensions/pi-typesafe/format_test.go +75 -0
- package/extensions/pi-typesafe/go.mod +10 -0
- package/extensions/pi-typesafe/go.sum +2 -0
- package/extensions/pi-typesafe/go.work +11 -0
- package/extensions/pi-typesafe/harness_test.go +200 -0
- package/extensions/pi-typesafe/ownmodel_test.go +100 -0
- package/extensions/pi-typesafe/review_test.go +134 -0
- package/extensions/pi-typesafe/tool.go +193 -0
- package/extensions/pi-typesafe/twin_test.go +28 -0
- package/libs/pi-typesafe-api/CREDITS.md +14 -0
- package/libs/pi-typesafe-api/LICENSE +22 -0
- package/libs/pi-typesafe-api/README.md +30 -0
- package/libs/pi-typesafe-api/ask.go +62 -0
- package/libs/pi-typesafe-api/ask_test.go +76 -0
- package/libs/pi-typesafe-api/auth.go +249 -0
- package/libs/pi-typesafe-api/auth_test.go +131 -0
- package/libs/pi-typesafe-api/backends.go +336 -0
- package/libs/pi-typesafe-api/backends_test.go +404 -0
- package/libs/pi-typesafe-api/batch.go +202 -0
- package/libs/pi-typesafe-api/batch_test.go +202 -0
- package/libs/pi-typesafe-api/battery_test.go +41 -0
- package/libs/pi-typesafe-api/calibrate.go +354 -0
- package/libs/pi-typesafe-api/calibrate_test.go +186 -0
- package/libs/pi-typesafe-api/client.go +615 -0
- package/libs/pi-typesafe-api/client_test.go +490 -0
- package/libs/pi-typesafe-api/credentials.go +252 -0
- package/libs/pi-typesafe-api/credentials_test.go +216 -0
- package/libs/pi-typesafe-api/doc.go +14 -0
- package/libs/pi-typesafe-api/errors.go +143 -0
- package/libs/pi-typesafe-api/evaluation.go +86 -0
- package/libs/pi-typesafe-api/evaluation_schema.json +264 -0
- package/libs/pi-typesafe-api/gaps_test.go +77 -0
- package/libs/pi-typesafe-api/go.mod +9 -0
- package/libs/pi-typesafe-api/go.sum +2 -0
- package/libs/pi-typesafe-api/helpers_test.go +169 -0
- package/libs/pi-typesafe-api/hostmodel/hostmodel.go +87 -0
- package/libs/pi-typesafe-api/json.go +299 -0
- package/libs/pi-typesafe-api/json_test.go +92 -0
- package/libs/pi-typesafe-api/ownmodel_test.go +79 -0
- package/libs/pi-typesafe-api/package.json +40 -0
- package/libs/pi-typesafe-api/provenance.json +18 -0
- package/libs/pi-typesafe-api/review_test.go +23 -0
- package/libs/pi-typesafe-api/schema.go +473 -0
- package/libs/pi-typesafe-api/schema_test.go +262 -0
- package/libs/pi-typesafe-api/testdata/tools/typebox-messages.mts +5 -0
- package/libs/pi-typesafe-api/testdata/typebox-messages.json +285 -0
- package/libs/pi-typesafe-api/twin_test.go +28 -0
- package/libs/pi-typesafe-api/ui/fakehost_test.go +548 -0
- package/libs/pi-typesafe-api/ui/keyprompt.go +115 -0
- package/libs/pi-typesafe-api/ui/login.go +106 -0
- package/libs/pi-typesafe-api/ui/twin_test.go +28 -0
- package/libs/pi-typesafe-api/ui/ui_test.go +285 -0
- package/libs/pi-typesafe-api/usage.go +366 -0
- package/libs/pi-typesafe-api/usage_test.go +139 -0
- package/libs/typesafe/CONTRACT.md +125 -0
- package/libs/typesafe/CREDITS.md +37 -0
- package/libs/typesafe/LICENSE +23 -0
- package/libs/typesafe/README.md +19 -0
- package/libs/typesafe/go.mod +3 -0
- package/libs/typesafe/libraries/ownmodel/backend_test.go +496 -0
- package/libs/typesafe/libraries/ownmodel/canon.go +190 -0
- package/libs/typesafe/libraries/ownmodel/convert.go +199 -0
- package/libs/typesafe/libraries/ownmodel/doc.go +15 -0
- package/libs/typesafe/libraries/ownmodel/equivalence_test.go +199 -0
- package/libs/typesafe/libraries/ownmodel/helpers_test.go +155 -0
- package/libs/typesafe/libraries/ownmodel/mutation_test.go +31 -0
- package/libs/typesafe/libraries/ownmodel/ownmodel.go +225 -0
- package/libs/typesafe/libraries/ownmodel/plan.go +442 -0
- package/libs/typesafe/libraries/ownmodel/run.go +288 -0
- package/libs/typesafe/libraries/ownmodel/schema_test.go +254 -0
- package/libs/typesafe/libraries/ownmodel/twins_test.go +169 -0
- package/libs/typesafe/libraries/ownmodel/utils_test.go +125 -0
- package/libs/typesafe/libraries/pigmodel/pigmodel.go +264 -0
- package/libs/typesafe/libraries/pigmodel/pigmodel_test.go +410 -0
- package/libs/typesafe/libraries/typesafe/answers.go +268 -0
- package/libs/typesafe/libraries/typesafe/api_response_test.go +113 -0
- package/libs/typesafe/libraries/typesafe/batch.go +80 -0
- package/libs/typesafe/libraries/typesafe/batch_test.go +133 -0
- package/libs/typesafe/libraries/typesafe/bench_test.go +71 -0
- package/libs/typesafe/libraries/typesafe/client.go +561 -0
- package/libs/typesafe/libraries/typesafe/client_test.go +495 -0
- package/libs/typesafe/libraries/typesafe/crosscheck_test.go +464 -0
- package/libs/typesafe/libraries/typesafe/crosscheck_workflowevals_test.go +219 -0
- package/libs/typesafe/libraries/typesafe/doc.go +27 -0
- package/libs/typesafe/libraries/typesafe/entry.go +142 -0
- package/libs/typesafe/libraries/typesafe/env.go +11 -0
- package/libs/typesafe/libraries/typesafe/errors.go +310 -0
- package/libs/typesafe/libraries/typesafe/errors_test.go +175 -0
- package/libs/typesafe/libraries/typesafe/helpers_test.go +294 -0
- package/libs/typesafe/libraries/typesafe/live_test.go +96 -0
- package/libs/typesafe/libraries/typesafe/logging.go +160 -0
- package/libs/typesafe/libraries/typesafe/logging_test.go +259 -0
- package/libs/typesafe/libraries/typesafe/marshal_test.go +112 -0
- package/libs/typesafe/libraries/typesafe/mutation_test.go +39 -0
- package/libs/typesafe/libraries/typesafe/questions.go +490 -0
- package/libs/typesafe/libraries/typesafe/questions_test.go +166 -0
- package/libs/typesafe/libraries/typesafe/regressions_test.go +159 -0
- package/libs/typesafe/libraries/typesafe/reliability_test.go +649 -0
- package/libs/typesafe/libraries/typesafe/retry.go +350 -0
- package/libs/typesafe/libraries/typesafe/retry_test.go +297 -0
- package/libs/typesafe/libraries/typesafe/runtime_test.go +26 -0
- package/libs/typesafe/libraries/typesafe/transport_test.go +163 -0
- package/libs/typesafe/libraries/typesafe/twins_test.go +127 -0
- package/libs/typesafe/libraries/typesafe/types_test.go +165 -0
- package/libs/typesafe/libraries/typesafe/version.go +10 -0
- package/libs/typesafe/package.json +37 -0
- package/libs/typesafe/provenance.json +49 -0
- package/package.json +42 -0
- package/port/PORT.md +98 -0
- package/port/accepted-gaps.json +3 -0
- package/port/golden/enable-confirm.jsonl +11 -0
- package/port/golden/enable-decline.jsonl +20 -0
- package/port/golden/enable-missing-key.jsonl +4 -0
- package/port/golden/login-shadow.jsonl +4 -0
- package/port/golden/logout-env-key.jsonl +6 -0
- package/port/golden/playground-cancel.jsonl +4 -0
- package/port/golden/playground-invalid-json.jsonl +5 -0
- package/port/golden/playground-invalid-questions.jsonl +5 -0
- package/port/golden/status-env-key.jsonl +6 -0
- package/port/golden/status-no-key.jsonl +6 -0
- package/port/golden/tool-disabled.jsonl +18 -0
- package/port/golden/trailing-words.jsonl +10 -0
- package/port/library-mutations.py +44 -0
- package/port/mutations.json +302 -0
- package/port/oracle/.env.example +4 -0
- package/port/oracle/CHANGELOG.md +91 -0
- package/port/oracle/CONTRIBUTING.md +35 -0
- package/port/oracle/LICENSE +21 -0
- package/port/oracle/README.md +159 -0
- package/port/oracle/docs/api.md +143 -0
- package/port/oracle/docs/ci-cd.md +97 -0
- package/port/oracle/examples/decision-extension.ts +41 -0
- package/port/oracle/extensions/index.js +2 -0
- package/port/oracle/package.json +89 -0
- package/port/oracle/scripts/dev-pi.mjs +23 -0
- package/port/oracle/scripts/live-smoke.mjs +35 -0
- package/port/oracle/src/ask.ts +42 -0
- package/port/oracle/src/auth.ts +171 -0
- package/port/oracle/src/backends.ts +196 -0
- package/port/oracle/src/batch.ts +170 -0
- package/port/oracle/src/calibrate.ts +237 -0
- package/port/oracle/src/client.ts +310 -0
- package/port/oracle/src/credentials.ts +136 -0
- package/port/oracle/src/errors.ts +53 -0
- package/port/oracle/src/extension.ts +204 -0
- package/port/oracle/src/index.ts +31 -0
- package/port/oracle/src/key-prompt.ts +51 -0
- package/port/oracle/src/login.ts +60 -0
- package/port/oracle/src/schema.ts +158 -0
- package/port/oracle/src/ui.ts +4 -0
- package/port/oracle/src/usage.ts +258 -0
- package/port/oracle/tests/ask.test.ts +63 -0
- package/port/oracle/tests/auth.test.ts +141 -0
- package/port/oracle/tests/backends.test.ts +380 -0
- package/port/oracle/tests/batch.test.ts +156 -0
- package/port/oracle/tests/calibrate.test.ts +144 -0
- package/port/oracle/tests/client.test.ts +499 -0
- package/port/oracle/tests/credentials.test.ts +144 -0
- package/port/oracle/tests/extension.test.ts +276 -0
- package/port/oracle/tests/key-prompt.test.ts +47 -0
- package/port/oracle/tests/login.test.ts +101 -0
- package/port/oracle/tests/schema.test.ts +85 -0
- package/port/oracle/tests/usage.test.ts +106 -0
- package/port/oracle/tsconfig.build.json +10 -0
- package/port/oracle/tsconfig.json +14 -0
- package/port/scenarios/enable-confirm.json +5 -0
- package/port/scenarios/enable-decline.json +3 -0
- package/port/scenarios/enable-missing-key.json +2 -0
- package/port/scenarios/login-shadow.json +2 -0
- package/port/scenarios/logout-env-key.json +3 -0
- package/port/scenarios/playground-cancel.json +2 -0
- package/port/scenarios/playground-invalid-json.json +2 -0
- package/port/scenarios/playground-invalid-questions.json +2 -0
- package/port/scenarios/status-env-key.json +3 -0
- package/port/scenarios/status-no-key.json +3 -0
- package/port/scenarios/tool-disabled.json +2 -0
- package/port/scenarios/trailing-words.json +5 -0
- package/port/upstream-tests.json +160 -0
- package/provenance.json +18 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "enabled-by-default",
|
|
4
|
+
"file": "extension.go",
|
|
5
|
+
"find": "s.enabled = os.Getenv(envEnabled) == \"1\"\n\ts.backend = backendFromEnv()\n\ts.registerTool()",
|
|
6
|
+
"replace": "s.enabled = true\n\ts.backend = backendFromEnv()\n\ts.registerTool()"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "session-start-keeps-optin",
|
|
10
|
+
"file": "extension.go",
|
|
11
|
+
"find": "\ts.enabled = os.Getenv(envEnabled) == \"1\"\n\tif backend := backendFromEnv(); backend != s.backend {",
|
|
12
|
+
"replace": "\tif backend := backendFromEnv(); backend != s.backend {"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "session-start-callout-always",
|
|
16
|
+
"file": "extension.go",
|
|
17
|
+
"find": "if err == nil && s.isEnabled() && report.Level == pitypesafe.LevelError",
|
|
18
|
+
"replace": "if err == nil && report.Level == pitypesafe.LevelError"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "session-start-callout-dropped",
|
|
22
|
+
"file": "extension.go",
|
|
23
|
+
"find": "s.callOut(ctx, \"start:\"+string(report.Level), \"TypeSafe is enabled but judgments are skipped. \"+report.Text)",
|
|
24
|
+
"replace": "_ = report"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "callout-not-deduplicated",
|
|
28
|
+
"file": "extension.go",
|
|
29
|
+
"find": "if s.calledOut == key {",
|
|
30
|
+
"replace": "if false {"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "callout-headless-notify",
|
|
34
|
+
"file": "extension.go",
|
|
35
|
+
"find": "\tif ctx.HasUI() {\n\t\tctx.Notify(text, \"warning\")\n\t\treturn\n\t}",
|
|
36
|
+
"replace": "\tctx.Notify(text, \"warning\")\n\treturn"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "callout-warning-level",
|
|
40
|
+
"file": "extension.go",
|
|
41
|
+
"find": "ctx.Notify(text, \"warning\")",
|
|
42
|
+
"replace": "ctx.Notify(text, \"error\")"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "disabled-message",
|
|
46
|
+
"file": "tool.go",
|
|
47
|
+
"find": "TypeSafe is disabled. Ask the operator to run /typesafe enable; do not enable it by editing configuration or environment files.",
|
|
48
|
+
"replace": "TypeSafe is disabled."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "disabled-check-removed",
|
|
52
|
+
"file": "tool.go",
|
|
53
|
+
"find": "\tenabled, consent := s.admit()\n\tif !enabled {",
|
|
54
|
+
"replace": "\t_, consent := s.admit()\n\tif false {"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "auth-callout-only-401",
|
|
58
|
+
"file": "tool.go",
|
|
59
|
+
"find": "rejected := safe.Code == pitypesafe.CodeHTTP && (safe.Status == 401 || safe.Status == 403)",
|
|
60
|
+
"replace": "rejected := false"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "auth-callout-403-missed",
|
|
64
|
+
"file": "tool.go",
|
|
65
|
+
"find": "(safe.Status == 401 || safe.Status == 403)",
|
|
66
|
+
"replace": "(safe.Status == 401)"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "auth-callout-text",
|
|
70
|
+
"file": "tool.go",
|
|
71
|
+
"find": "TypeSafe is not authenticated (",
|
|
72
|
+
"replace": "TypeSafe auth problem ("
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "tool-result-details-dropped",
|
|
76
|
+
"file": "tool.go",
|
|
77
|
+
"find": "Details: json.RawMessage(details)",
|
|
78
|
+
"replace": "Details: json.RawMessage(details[:0])"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "tool-content-not-json",
|
|
82
|
+
"file": "tool.go",
|
|
83
|
+
"find": "sdk.ToolResult{Content: string(content)",
|
|
84
|
+
"replace": "sdk.ToolResult{Content: string(content[:0])"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "logout-keeps-enabled",
|
|
88
|
+
"file": "command.go",
|
|
89
|
+
"find": "\t\ts.resetClient()\n\t\ts.setEnabled(false)\n\t\tif removed {",
|
|
90
|
+
"replace": "\t\ts.resetClient()\n\t\tif removed {"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "logout-keeps-auth-state",
|
|
94
|
+
"file": "command.go",
|
|
95
|
+
"find": "\t\tpitypesafe.ClearAuthState()\n\t\ts.resetClient()",
|
|
96
|
+
"replace": "\t\ts.resetClient()"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "logout-env-note-dropped",
|
|
100
|
+
"file": "command.go",
|
|
101
|
+
"find": "text += \" TYPESAFE_API_KEY is still set in the environment.\"",
|
|
102
|
+
"replace": "_ = text"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "logout-message",
|
|
106
|
+
"file": "command.go",
|
|
107
|
+
"find": "\"No stored key to remove.\"",
|
|
108
|
+
"replace": "\"Nothing to remove.\""
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "disable-keeps-enabled",
|
|
112
|
+
"file": "command.go",
|
|
113
|
+
"find": "\tcase \"disable\":\n\t\ts.setEnabled(false)",
|
|
114
|
+
"replace": "\tcase \"disable\":\n\t\t_ = 0"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"name": "disable-message",
|
|
118
|
+
"file": "command.go",
|
|
119
|
+
"find": "TypeSafe disabled for future agent calls. In-flight requests are not cancelled.",
|
|
120
|
+
"replace": "Disabled."
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "usage-text",
|
|
124
|
+
"file": "command.go",
|
|
125
|
+
"find": "\"Usage: /typesafe \"+strings.Join(actions, \" | \")",
|
|
126
|
+
"replace": "\"Usage: /typesafe\""
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "usage-level",
|
|
130
|
+
"file": "command.go",
|
|
131
|
+
"find": "strings.Join(actions, \" | \"), \"warning\")",
|
|
132
|
+
"replace": "strings.Join(actions, \" | \"), \"info\")"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "headless-message",
|
|
136
|
+
"file": "command.go",
|
|
137
|
+
"find": "This command needs interactive Pi. For headless tool use, explicitly set PI_TYPESAFE_ENABLED=1 and TYPESAFE_API_KEY before launching Pi.",
|
|
138
|
+
"replace": "Needs UI."
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "login-shadow-check-removed",
|
|
142
|
+
"file": "command.go",
|
|
143
|
+
"find": "if pitypesafe.EnvKeySet() {\n\t\t\treport(\"TYPESAFE_API_KEY is set in the environment and takes precedence",
|
|
144
|
+
"replace": "if false {\n\t\t\treport(\"TYPESAFE_API_KEY is set in the environment and takes precedence"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"name": "login-cancel-message",
|
|
148
|
+
"file": "command.go",
|
|
149
|
+
"find": "Login cancelled; nothing was saved.",
|
|
150
|
+
"replace": "Cancelled."
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "enable-missing-key-check-removed",
|
|
154
|
+
"file": "command.go",
|
|
155
|
+
"find": "\t\tcase pitypesafe.KeyMissing:\n\t\t\treport(\"Run /typesafe login first",
|
|
156
|
+
"replace": "\t\tcase pitypesafe.KeyNotRequired:\n\t\t\treport(\"Run /typesafe login first"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "enable-unusable-message",
|
|
160
|
+
"file": "command.go",
|
|
161
|
+
"find": "The stored key cannot be used. ",
|
|
162
|
+
"replace": ""
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "enable-confirm-ignored",
|
|
166
|
+
"file": "command.go",
|
|
167
|
+
"find": "\t\tif ok {\n\t\t\ts.setEnabled(true)",
|
|
168
|
+
"replace": "\t\tif ok || true {\n\t\t\ts.setEnabled(true)"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "enable-never-confirms",
|
|
172
|
+
"file": "command.go",
|
|
173
|
+
"find": "ok, err := ctx.Confirm(\"Enable TypeSafe for this session?\", disclosure(backend))",
|
|
174
|
+
"replace": "ok, err := true, error(nil)"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"name": "enable-title",
|
|
178
|
+
"file": "command.go",
|
|
179
|
+
"find": "\"Enable TypeSafe for this session?\"",
|
|
180
|
+
"replace": "\"Enable?\""
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"name": "setup-current-label",
|
|
184
|
+
"file": "command.go",
|
|
185
|
+
"find": "current = \"configured via \" + pitypesafe.KeySourceLabel(situation)",
|
|
186
|
+
"replace": "current = \"configured\""
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "playground-invalid-json-message",
|
|
190
|
+
"file": "command.go",
|
|
191
|
+
"find": "Invalid JSON. Keep quoted strings on one line; nothing was sent.",
|
|
192
|
+
"replace": "Invalid JSON."
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"name": "playground-confirm-before-validate",
|
|
196
|
+
"file": "command.go",
|
|
197
|
+
"find": "\tvalidated, err := pitypesafe.PrepareEvaluationRequest(request, pitypesafe.PrepareOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\tconsent := s.consentNow()\n\tok, err := ctx.Confirm(\"Send this TypeSafe request?\", disclosure(s.currentBackend()))\n\tif err != nil || !ok {\n\t\treturn err\n\t}",
|
|
198
|
+
"replace": "\tconsent := s.consentNow()\n\tok, err := ctx.Confirm(\"Send this TypeSafe request?\", disclosure(s.currentBackend()))\n\tif err != nil || !ok {\n\t\treturn err\n\t}\n\tvalidated, err := pitypesafe.PrepareEvaluationRequest(request, pitypesafe.PrepareOptions{})\n\tif err != nil {\n\t\treturn err\n\t}"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "test-confirm-title",
|
|
202
|
+
"file": "command.go",
|
|
203
|
+
"find": "\"Send this TypeSafe request?\"",
|
|
204
|
+
"replace": "\"Send?\""
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"name": "status-model-name",
|
|
208
|
+
"file": "command.go",
|
|
209
|
+
"find": "model := pitypesafe.DefaultModelID(pitypesafe.DefaultBackend)",
|
|
210
|
+
"replace": "model := \"jev\""
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "status-warning-level",
|
|
214
|
+
"file": "command.go",
|
|
215
|
+
"find": "if auth.Level == pitypesafe.LevelError && enabled {",
|
|
216
|
+
"replace": "if auth.Level == pitypesafe.LevelError {"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"name": "status-session-cap",
|
|
220
|
+
"file": "command.go",
|
|
221
|
+
"find": "\"Session %d/%d attempts, %d successful",
|
|
222
|
+
"replace": "\"Session %d/%d tries, %d successful"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"name": "result-not-appended",
|
|
226
|
+
"file": "command.go",
|
|
227
|
+
"find": "return ctx.AppendEntry(entryType, json.RawMessage(details))",
|
|
228
|
+
"replace": "_ = details\n\treturn nil"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"name": "entry-type",
|
|
232
|
+
"file": "extension.go",
|
|
233
|
+
"find": "entryType = \"typesafe-result\"",
|
|
234
|
+
"replace": "entryType = \"typesafe-results\""
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"name": "backend-switch-keeps-consent",
|
|
238
|
+
"file": "command.go",
|
|
239
|
+
"find": "\ts.consent++\n\ts.enabled = false",
|
|
240
|
+
"replace": "\ts.consent++"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"name": "ownmodel-disclosure-typesafe",
|
|
244
|
+
"file": "tool.go",
|
|
245
|
+
"find": "if backend == pitypesafe.BackendOwnModel {\n\t\treturn ownModelMsg",
|
|
246
|
+
"replace": "if false {\n\t\treturn ownModelMsg"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "format-noul",
|
|
250
|
+
"file": "format.go",
|
|
251
|
+
"find": "\"%s: P(yes) = %s\"",
|
|
252
|
+
"replace": "\"%s: p = %s\""
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"name": "format-choice",
|
|
256
|
+
"file": "format.go",
|
|
257
|
+
"find": "%s: %s \u00b7 confidence %s\", label, jsonString(choice)",
|
|
258
|
+
"replace": "%s: %s (confidence %s)\", label, jsonString(choice)"
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"name": "format-expanded-ignored",
|
|
262
|
+
"file": "format.go",
|
|
263
|
+
"find": "if expanded && answer[\"type\"] != \"noul\" {",
|
|
264
|
+
"replace": "if false {"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"name": "render-partial",
|
|
268
|
+
"file": "tool.go",
|
|
269
|
+
"find": "\"TypeSafe \u00b7 waiting for response\"",
|
|
270
|
+
"replace": "\"waiting\""
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"name": "render-call-count",
|
|
274
|
+
"file": "tool.go",
|
|
275
|
+
"find": "len(questions), where)",
|
|
276
|
+
"replace": "len(questions)+1, where)"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"name": "consent-generation-ignored",
|
|
280
|
+
"file": "extension.go",
|
|
281
|
+
"find": "\tif s.consent != consent {\n\t\treturn nil, errDestinationChanged",
|
|
282
|
+
"replace": "\tif false {\n\t\treturn nil, errDestinationChanged"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"name": "backend-switch-keeps-generation",
|
|
286
|
+
"file": "command.go",
|
|
287
|
+
"find": "\ts.backend = choice\n\ts.consent++",
|
|
288
|
+
"replace": "\ts.backend = choice"
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"name": "trailing-words-are-an-action",
|
|
292
|
+
"file": "command.go",
|
|
293
|
+
"find": "\taction := text\n\tif action == \"\" {",
|
|
294
|
+
"replace": "\taction := text\n\tif len(fields) > 0 {\n\t\taction = fields[0]\n\t}\n\tif action == \"\" {"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"name": "ownmodel-callout-blames-key",
|
|
298
|
+
"file": "tool.go",
|
|
299
|
+
"find": "\t\tif s.currentBackend() == pitypesafe.BackendOwnModel {\n\t\t\t// The own-model",
|
|
300
|
+
"replace": "\t\tif false {\n\t\t\t// The own-model"
|
|
301
|
+
}
|
|
302
|
+
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
<!-- Empty. Next release starts here. -->
|
|
6
|
+
|
|
7
|
+
## 0.8.0
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- A `commandcode` backend for the same Jev decisions protocol: `createTypeSafe({ backend: "commandcode" })` sends judgments to `api.commandcode.ai` under `/provider/v1/systemone` with the key from `COMMANDCODE_API_KEY` and the model `typesafe/jev`; its public model list does not verify a key.
|
|
12
|
+
- `backend` accepts a caller-supplied endpoint object wherever a backend name is accepted (`createTypeSafe`, `keySituation`, `resolveApiKey`, `authState`, `ensureApiKey`, `safeError`): an endpoint names its own `label`, `host`, `keyEnv`, and optionally `path`, `defaultModel`, and model-list fields, is validated on every call, never reads `TYPESAFE_API_KEY` or the login store, and is never added to the registry. An invalid backend makes `authState` and `keySituation` throw `configuration`; validate user input with `resolveBackend` first.
|
|
13
|
+
- `resolveBackend(nameOrEndpoint)` resolves either form to the validated backend the client uses, and `backendHost(nameOrEndpoint)` reports the destination host for consent text, alongside the `BackendEndpoint`, `BackendSpec`, and `ResolvedBackend` types.
|
|
14
|
+
- `TypeSafeBackend` now includes `"commandcode"`; a consumer with an exhaustive `switch` over it sees a new member.
|
|
15
|
+
|
|
16
|
+
## 0.7.4
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- A bare Jev model id is mapped to the backend's own form before it is sent — on OpenRouter `jev-latest` goes as `~typesafe/jev-latest` and `jev-1.13` as `typesafe/jev-1.13`, for the client default and a per-request `model` alike — so OpenRouter no longer answers 400, and `/typesafe status` reports the model the configured backend actually sends.
|
|
21
|
+
|
|
22
|
+
## 0.7.3
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Dependency updates: `typebox` 1.3.31 to 1.3.34 (runtime); `typescript` 6.0.3 to 7.0.2, `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui` 0.85.1 to 0.86.1, `@types/node` 22.20.3 to 22.20.4, and `tsx` 4.23.13 to 4.23.15 (development). No change to the public API.
|
|
27
|
+
|
|
28
|
+
## 0.7.2
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- HTTP advice is backend-aware: `safeError(error, backend?)` names the backend's key variable on a 401 (`Check OPENROUTER_API_KEY.` on OpenRouter, `Check TYPESAFE_API_KEY.` by default), a 402 now says `Insufficient credits. Add credits at https://openrouter.ai/credits.` on OpenRouter and `Check your account balance.` elsewhere without marking the key unusable, and a 429 appends `Retry after <n> seconds.` when the response carries a numeric `Retry-After` header. One-argument `safeError` calls are unchanged.
|
|
33
|
+
|
|
34
|
+
## 0.7.1
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- `listModels()` asks each backend for its own model list. It requested the SDK's `/v1/models` on every backend, so on OpenRouter it fetched an HTML page and always failed; it now uses `/api/v1/models` and reads the list from OpenRouter's `data` field (#11).
|
|
39
|
+
- `listModels()` on OpenRouter returns model ids (`vendor/model`), the values `model:` accepts, instead of display names.
|
|
40
|
+
- A public model list no longer proves a key. OpenRouter serves its list without checking the key, so a successful `listModels()` there leaves `authState({ backend: "openrouter" })` unverified rather than recording a garbage key as verified.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- `BackendConfig` gains optional `modelsPath`, `modelsField`, `modelsIdField`, and `modelsVerifyKey`, documented in the API reference.
|
|
45
|
+
|
|
46
|
+
## 0.7.0
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Key reporting and login can name the judgment backend: `keySituation(backend)`, `resolveApiKey(backend)`, `authState({ backend })`, and `ensureApiKey(ctx, { backend })` read the backend's own environment variable, `describeAuth` labels the key by backend and names the variable to set, and `AuthState` carries `backend`. Before, every surface reported the TypeSafe key, so an OpenRouter user saw "TypeSafe key: missing" while judgments ran, and `ensureApiKey` opened the TypeSafe login and verified the pasted key against api.typesafe.ai (#9).
|
|
51
|
+
- `createTypeSafe({ backend: "openrouter" })` no longer falls back to `TYPESAFE_API_KEY` or the login store when `OPENROUTER_API_KEY` is unset; a TypeSafe key was being sent to OpenRouter.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- `DEFAULT_BACKEND` export and a `label` on every `DECISIONS_BACKENDS` entry.
|
|
56
|
+
|
|
57
|
+
### Docs
|
|
58
|
+
|
|
59
|
+
- Document the `backend` option, `DECISIONS_BACKENDS`, and which key each backend reads in the README and the API reference; the OpenRouter backend shipped in 0.6.0 without either.
|
|
60
|
+
|
|
61
|
+
## 0.6.2
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- Describe every field the agent authors in the `typesafe_evaluate` schema (`state`, `questions`, `type`, `instructions`, `criteria`, `model`) and show one request payload in the prompt guidelines, so the first call no longer has to fail to learn the shape (#6, #7).
|
|
66
|
+
|
|
67
|
+
## 0.6.1
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- Send OpenRouter judgments to `/api/alpha/decisions` instead of the TypeSafe SDK's default `/v1/systemone` path, while preserving caller-supplied transports (#2).
|
|
72
|
+
- Keep the lockfile's root package version aligned with the published package.
|
|
73
|
+
- Build declarations before checking public-API examples so `npm run check` works from a clean checkout.
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- Contributor CI for supported Node versions on Linux and macOS, workflow lint, installed-package smoke tests, and a combined `CI passed` check.
|
|
78
|
+
- Verified package artifacts, tag-triggered draft GitHub releases, weekly dependency updates, and contributor/release guidance; npm publication remains manual.
|
|
79
|
+
|
|
80
|
+
## 0.6.0
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
|
|
84
|
+
- `backend` option on `TypeSafeOptions` to route judgments to different services (`"typesafe"` or `"openrouter"`).
|
|
85
|
+
- `DECISIONS_BACKENDS` registry mapping backend names to `{ host, keyEnv }` configs.
|
|
86
|
+
- Backend-specific env var resolution: `OPENROUTER_API_KEY` for openrouter, `TYPESAFE_API_KEY` for typesafe.
|
|
87
|
+
- Default model changes per backend: `jev-latest` for typesafe, `typesafe/jev-1.13` for openrouter.
|
|
88
|
+
|
|
89
|
+
## 0.5.0
|
|
90
|
+
|
|
91
|
+
- Initial tracked release.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Contributing to pi-typesafe
|
|
2
|
+
|
|
3
|
+
pi-typesafe is the TypeSafe client other Pi extensions build on: key storage, consent, budgets, validated responses, and the `typesafe_evaluate` tool. A small, predictable surface is the feature. Please keep it that way.
|
|
4
|
+
|
|
5
|
+
## Report
|
|
6
|
+
|
|
7
|
+
- **A bug.** Use the "Bug" template with the Pi and pi-typesafe versions, the error text Pi showed, and the smallest sequence that reproduces it. `/typesafe status` output helps.
|
|
8
|
+
- **A gap for extension authors.** Use the "Extension author request" template: what you are building, what the client does not let you do, and the smallest API that would.
|
|
9
|
+
- **Security.** A way to expose a key, an upstream response body, or submitted content: use the private security advisory, not a public issue.
|
|
10
|
+
|
|
11
|
+
## Change
|
|
12
|
+
|
|
13
|
+
1. `npm ci`, then `npm run check` (build, typecheck, offline tests with an injected `fetch`). It must pass before and after your change.
|
|
14
|
+
2. Behaviour changes come with tests in `tests/`. Everything is testable offline through the `fetch` option; no test may need a key or the network.
|
|
15
|
+
3. `npm run test:live` sends one billable request with your own key. Run it when you touch the transport or the response validation and say so in the PR.
|
|
16
|
+
|
|
17
|
+
## Rules the code keeps
|
|
18
|
+
|
|
19
|
+
- **The public API is `src/index.ts`.** Adding an export needs a line in [docs/api.md](docs/api.md); removing or changing one needs a minor version bump until 1.0 and a note in the release.
|
|
20
|
+
- **No new runtime dependencies without a reason in the PR.** Today: `@typesafe-ai/sdk` and `typebox`.
|
|
21
|
+
- **Nothing from upstream reaches the user unvalidated.** Responses are checked against the questions before a caller sees them; an invalid response is an error, never a partial answer.
|
|
22
|
+
- **Errors carry a code and a safe message.** Never a response body, never the key, never the submitted state.
|
|
23
|
+
- **Keys are owner-only and never returned.** `resolveApiKey` tells you where a key came from, not what it is.
|
|
24
|
+
- **Consent and budget are enforced here, not in callers.** `maxRequests` counts attempts, including failures; a caller cannot exceed it by retrying. Daily caps live in the same client so a script cannot opt out by forgetting them.
|
|
25
|
+
|
|
26
|
+
## Commits and pull requests
|
|
27
|
+
|
|
28
|
+
- One change per commit, a message that says what and why, no tool or AI attribution lines.
|
|
29
|
+
- One topic per PR; update the README in the same PR when behaviour changes.
|
|
30
|
+
- Releases are cut by the maintainer; do not bump `package.json` in a PR unless the maintainer requests it. Release bumps update `package-lock.json` and `CHANGELOG.md` too.
|
|
31
|
+
- GitHub Actions checks supported Node versions, Linux and macOS, and the installed npm package without API keys. **CI passed** is the combined merge gate; see [CI and continuous delivery](docs/ci-cd.md) for branch-rule setup and the manual release process.
|
|
32
|
+
|
|
33
|
+
## Where to ask
|
|
34
|
+
|
|
35
|
+
Issues here, or the Pi and TypeSafe Discord servers. Critique the code, not the person.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryan Gapac
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# pi-typesafe
|
|
2
|
+
|
|
3
|
+
[](https://github.com/DevMortimer/pi-typesafe/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
[Jev](https://typesafe.ai) inside [Pi](https://pi.dev). Jev is TypeSafe's judgment model: send it some state and typed questions and it returns probabilities instead of prose, in well under a second, for a fraction of a cent. This package gives Pi three things built on it:
|
|
6
|
+
|
|
7
|
+
- **A tool for the agent.** `typesafe_evaluate` hands small structured judgments (classify, triage, compare, score) to Jev and returns calibrated numbers in one batched call.
|
|
8
|
+
- **A playground.** `/typesafe test` and `/typesafe playground` run requests from the terminal without touching the model's context.
|
|
9
|
+
- **A typed API for other extensions.** One client, one key store, one login prompt, so extensions such as [pi-warden](https://github.com/DevMortimer/pi-warden) do not each ask for a key — and so a long run can tell you whether Jev was reachable and what it cost.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
Independent project. Not affiliated with TypeSafe AI or the Pi authors.
|
|
14
|
+
|
|
15
|
+
## In one minute
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pi install npm:pi-typesafe
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then, inside Pi:
|
|
22
|
+
|
|
23
|
+
1. `/typesafe login` and paste a key from [console.typesafe.ai](https://console.typesafe.ai). Input is hidden; the key is verified against the API and saved to `~/.pi/agent/pi-typesafe/auth.json` with owner-only permissions.
|
|
24
|
+
2. `/typesafe test` sends one built-in sample request and shows the answers.
|
|
25
|
+
3. `/typesafe enable` lets the agent call the tool for this session, after you confirm the data notice.
|
|
26
|
+
|
|
27
|
+
Requires Pi 0.85 or newer and Node.js 22.19 or newer. Usage is billed to your TypeSafe account. For CI or scripts set `TYPESAFE_API_KEY` in the environment instead; it takes precedence over the stored key. Do not paste the key into chat, command arguments, or project files.
|
|
28
|
+
|
|
29
|
+
## The three question types
|
|
30
|
+
|
|
31
|
+
Jev answers three kinds of question about the state you send. Every question in a request runs in parallel and in isolation, so adding questions barely changes the latency.
|
|
32
|
+
|
|
33
|
+
| Type | Asks | Returns |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| **Choice** | Which of these options fits? | the chosen key, a probability per option, confidence |
|
|
36
|
+
| **Score** | Where on this ordered rubric does it sit? | a position (may be fractional), probabilities, confidence |
|
|
37
|
+
| **Noul** | Is this statement true? | a probability from 0 to 1 |
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"state": { "message": "I was charged twice. Please help today." },
|
|
42
|
+
"questions": {
|
|
43
|
+
"team": { "type": "choice", "instructions": "Which team should handle this?",
|
|
44
|
+
"criteria": { "billing": "Charges and payments", "technical": "Software failures", "other": "None of these" } },
|
|
45
|
+
"urgent": { "type": "noul", "instructions": "Does the sender request help today?" },
|
|
46
|
+
"frustration": { "type": "score", "instructions": "How frustrated does the sender sound?",
|
|
47
|
+
"criteria": ["Neutral request", "Frustrated but civil", "Explicit anger or threats"] }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Read probabilities and confidence alongside the answer. Confidence describes how concentrated the distribution is; it is not proof of correctness or permission to act.
|
|
53
|
+
|
|
54
|
+
## The agent tool
|
|
55
|
+
|
|
56
|
+
`typesafe_evaluate` is registered at startup but **disabled until you run `/typesafe enable`** in the session. For automated or headless runs, set `PI_TYPESAFE_ENABLED=1` explicitly.
|
|
57
|
+
|
|
58
|
+
The tool accepts JSON `state` plus 1 to 32 questions and returns answers, model, token usage, and elapsed time. Good uses: triaging a list of issues in one call, deciding which of several files a change belongs to, checking whether a reply answers the question that was asked, scoring candidates against a rubric you wrote. Bad uses: anything that needs reasoning across steps, or a single question that mixes several judgments.
|
|
59
|
+
|
|
60
|
+
## Writing questions that work
|
|
61
|
+
|
|
62
|
+
The question text is the whole program. Jev answers exactly what is asked, so ambiguity shows up as a middling probability rather than an error.
|
|
63
|
+
|
|
64
|
+
- **Ask about what the state says, not what you would conclude.** A report that says "happens every time" scored `P(yes) = 0.36` for `Is the bug reproducible from the text?` because that can also mean "could a reader reproduce it using only this text?". `Does the reporter state that the problem occurs consistently?` is the intended question.
|
|
65
|
+
- **Describe situations in Score levels, not degrees.** `"Workaround exists"` is checkable; `"medium"` is not.
|
|
66
|
+
- **Include a no-match option** in a Choice (`other`, `unclear`) when nothing may fit; the model cannot pick an option you omitted.
|
|
67
|
+
- **One judgment per question.** Split independent dimensions into separate questions and batch them in one request; they run in parallel and cannot see each other.
|
|
68
|
+
- **Name the state fields you mean** with backticks (`` `report.body` ``) when the state has several parts.
|
|
69
|
+
|
|
70
|
+
The [TypeSafe docs](https://docs.typesafe.ai/primitives) cover each primitive in detail.
|
|
71
|
+
|
|
72
|
+
## Availability and spend
|
|
73
|
+
|
|
74
|
+
An enabled extension with no usable key looks exactly like a working one — one evaluation ran keyless for hours before anyone noticed. Three answers exist now, and `/typesafe status` prints all of them:
|
|
75
|
+
|
|
76
|
+
- **Is Jev reachable?** `authState()` reports the key source, whether it has been accepted, and the last failure that degraded it; `describeAuth()` turns that into a level and one safe line. The extension calls both at session start and after an authentication rejection, so a headless run says judgments are skipped instead of quietly falling back to its offline path.
|
|
77
|
+
- **What has this run cost?** `getSpend()` returns session counters, today's persisted counters, and the cap currently reached. Cost is estimated from input tokens only, because output is free.
|
|
78
|
+
- **When does it stop?** `maxRequests` bounds a client instance. Three caps bound a local day, survive restarts, and stop a request before it is submitted:
|
|
79
|
+
|
|
80
|
+
| Option | Environment | Bounds |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| `maxRequestsPerDay` | `PI_TYPESAFE_MAX_REQUESTS_PER_DAY` | requests |
|
|
83
|
+
| `maxInputTokensPerDay` | `PI_TYPESAFE_MAX_INPUT_TOKENS_PER_DAY` | input tokens |
|
|
84
|
+
| `maxUsdPerDay` | `PI_TYPESAFE_MAX_USD_PER_DAY` | estimated spend |
|
|
85
|
+
|
|
86
|
+
The environment may lower an explicit cap but never raise it. A reached cap raises a `budget` error naming the cap, the amount used, and the day.
|
|
87
|
+
|
|
88
|
+
Need more than one request? `evaluateAll(request)` asks any number of questions about one state (over 32 are chunked and fanned out), and `evaluateMany(requests)` runs several requests at once. Both preserve order, bound concurrency, never throw, and stop submitting once the budget is gone. [`pi-typesafe/calibrate`](docs/api.md#calibration-pi-typesafecalibrate) turns labelled cases into thresholds with AUC, a sweep, and a replay runner.
|
|
89
|
+
|
|
90
|
+
## Commands
|
|
91
|
+
|
|
92
|
+
| Command | Effect |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `/typesafe login` | Enter and verify an API key (hidden input), then store it |
|
|
95
|
+
| `/typesafe logout` | Delete the stored key and disable the tool |
|
|
96
|
+
| `/typesafe setup` | Check which key is in use; starts login if none |
|
|
97
|
+
| `/typesafe status` | Opt-in state, key state, session and today's counters, cost estimate, any reached cap |
|
|
98
|
+
| `/typesafe enable` | Confirm the data notice and allow agent tool calls this session |
|
|
99
|
+
| `/typesafe disable` | Stop future agent tool calls |
|
|
100
|
+
| `/typesafe test` | Send one built-in sample request |
|
|
101
|
+
| `/typesafe playground` | Edit request JSON in Pi's editor, confirm, view results |
|
|
102
|
+
|
|
103
|
+
Playground and test results are shown in the terminal only; they do not enter the model's context.
|
|
104
|
+
|
|
105
|
+
## Questions people ask
|
|
106
|
+
|
|
107
|
+
**Do I need a key to install?**
|
|
108
|
+
Install works without one; nothing is sent until you log in and enable the tool. Jev is new and access may be limited at the moment. Keys come from [console.typesafe.ai](https://console.typesafe.ai).
|
|
109
|
+
|
|
110
|
+
**What does a request cost?**
|
|
111
|
+
Whatever TypeSafe bills for the input tokens of your state and questions; output is free. At the listed rate ($42 per billion input tokens at the time of writing) a few hundred tokens cost well under a hundredth of a cent. The per-session cap is 20 attempts, and `maxUsdPerDay` stops a long run at a number you choose.
|
|
112
|
+
|
|
113
|
+
**Why not just ask the main model?**
|
|
114
|
+
The main model can answer any of these questions in prose. It is slower, costs more per call, and grades its own work. Jev returns a calibrated number your code or the agent can branch on, in a quarter of a second, from a separate model. That matters most when the same question is asked many times: every tool call, every file, every issue in a list.
|
|
115
|
+
|
|
116
|
+
**What is sent?**
|
|
117
|
+
Only the state and questions you (or the agent, once enabled) submit, to `https://api.typesafe.ai` only. No files, conversation history, or telemetry. Error messages never include upstream response bodies, headers, keys, or your submitted state.
|
|
118
|
+
|
|
119
|
+
**Limits?**
|
|
120
|
+
Per request: 32 questions and 64 KiB of JSON. Per session: 20 attempts, 15-second timeout, no automatic retries. Per day: no cap unless you set one. Session limits reset when a session starts or reloads; daily counters live in `~/.pi/agent/pi-typesafe/usage.json` and roll over at local midnight. The SDK's `TYPESAFE_BASE_URL` and `TYPESAFE_LOG_LEVEL` overrides are ignored.
|
|
121
|
+
|
|
122
|
+
## For extension authors
|
|
123
|
+
|
|
124
|
+
Import the library from your own extension. It has no dependency on Pi and is safe in tests.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { ask, createTypeSafe, choice, noul, score } from "pi-typesafe";
|
|
128
|
+
|
|
129
|
+
const typesafe = createTypeSafe({ maxRequests: 5, maxUsdPerDay: 1 }); // key: TYPESAFE_API_KEY, else the login store
|
|
130
|
+
const answer = await ask(typesafe, {
|
|
131
|
+
state: { title: "Login fails after update", body: "..." },
|
|
132
|
+
questions: {
|
|
133
|
+
area: choice("Which area does this report concern?", { auth: "Sign-in", ui: "Layout", other: null }),
|
|
134
|
+
duplicate: noul("Does the report describe the same defect as `known_issue`?"),
|
|
135
|
+
severity: score("How severe is the defect?", ["Cosmetic", "Workaround exists", "Blocking"]),
|
|
136
|
+
},
|
|
137
|
+
}, { timeoutMs: 5_000 });
|
|
138
|
+
if (!answer.ok) return { skipped: answer.errorCode === "budget" }; // never throws
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Your extension owns its own user consent and budget; `/typesafe enable` applies only to this package's tool. Check `authState()` rather than your own consent flag before you report that judgments are on.
|
|
142
|
+
|
|
143
|
+
Judgments can also go through OpenRouter: `createTypeSafe({ backend: "openrouter" })` sends them to `openrouter.ai` with the key from `OPENROUTER_API_KEY`. Command Code serves the same protocol: `createTypeSafe({ backend: "commandcode" })` sends them to `api.commandcode.ai` with the key from `COMMANDCODE_API_KEY` and the model `typesafe/jev`. Or pass a `BackendEndpoint` object as `backend` to name any host that serves the protocol: it brings its own key variable, never reads `TYPESAFE_API_KEY`, and `backendHost()` reports its destination host for your consent text. These backends have no login store, so `/typesafe login` does not apply to them. Pass the same `backend` to `authState`, `keySituation`, and `ensureApiKey`, or the status you report describes the TypeSafe key while the requests use another one. The `/typesafe` commands and the `typesafe_evaluate` tool always use the TypeSafe backend. Every export — the client, `ask`, batching, the usage ledger, auth state, and the `pi-typesafe/calibrate` and `pi-typesafe/ui` entry points — is in [docs/api.md](docs/api.md).
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npm ci
|
|
149
|
+
npm run check # build, typecheck, offline tests
|
|
150
|
+
cp .env.example .env # add your key locally; .env is git-ignored
|
|
151
|
+
npm run test:live # one billable sample request
|
|
152
|
+
npm run dev:pi # start Pi with only this working tree as extension (.env optional)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Pull requests run key-free checks on Linux and macOS, supported Node versions, and the installed npm package. Version tags prepare a draft release; npm publishing stays manual. See [Contributing](CONTRIBUTING.md) and [CI and continuous delivery](docs/ci-cd.md).
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT
|