pyyol 1.9.0 → 1.10.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/dist/cli.d.ts +24 -0
- package/dist/cli.js +77 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -0
- package/dist/instrument.d.ts +29 -3
- package/dist/instrument.js +307 -102
- package/dist/movetools.d.ts +141 -0
- package/dist/movetools.js +486 -0
- package/dist/pricing.d.ts +16 -4
- package/dist/pricing.js +55 -8
- package/dist/scaffold.d.ts +74 -0
- package/dist/scaffold.js +276 -0
- package/dist/telemetry.d.ts +24 -0
- package/dist/telemetry.js +62 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -2
- package/rules/llms-full.txt +687 -26
- package/skill/SKILL.md +1 -0
- package/skill/references/telemetry.md +55 -0
package/dist/cli.d.ts
CHANGED
|
@@ -15,5 +15,29 @@ export declare function connectionToken(a: Args, c: creds.Credentials | null): {
|
|
|
15
15
|
token: string;
|
|
16
16
|
usingAgentKey: boolean;
|
|
17
17
|
};
|
|
18
|
+
/** True if the agent's entry file appears to send a system prompt, false if not, null if unknown.
|
|
19
|
+
*
|
|
20
|
+
* A shallow source scan, deliberately: it recognises Anthropic's `system=` and OpenAI's system /
|
|
21
|
+
* developer roles. Being approximate is fine because a wrong answer here is a HINT, not a
|
|
22
|
+
* decision — the authoritative value is the `scaffold` field on a real decision, which is what the
|
|
23
|
+
* unknown case points at.
|
|
24
|
+
*
|
|
25
|
+
* "developer" is included because it is OpenAI's newer name for the system role, and missing it
|
|
26
|
+
* would tell a correctly built agent it is ineligible — worse than saying nothing. */
|
|
27
|
+
export declare function scaffoldHint(entry: string | undefined): Promise<boolean | null>;
|
|
28
|
+
/** Report whether this agent will actually earn Verified, and if not, exactly why.
|
|
29
|
+
*
|
|
30
|
+
* Three things decide it and each fails silently on its own: routing (without it the platform sees
|
|
31
|
+
* no calls at all), a system prompt (without one the harness cannot be fingerprinted, so the agent
|
|
32
|
+
* is excluded from paired model comparison), and coverage (the share of decisions actually proven,
|
|
33
|
+
* against the threshold the verified tier requires).
|
|
34
|
+
*
|
|
35
|
+
* An agent can run perfectly and earn nothing. Nothing else in the toolchain says so, and learning
|
|
36
|
+
* it from an empty leaderboard row weeks later is the failure this prevents. */
|
|
37
|
+
export declare function printVerifiedReadiness(base: string, c: {
|
|
38
|
+
accessToken?: string;
|
|
39
|
+
} | null, cfg: {
|
|
40
|
+
entry?: string;
|
|
41
|
+
} | null): Promise<void>;
|
|
18
42
|
export declare function apiRequest(method: string, url: string, token: string, body: unknown): Promise<[number, any]>;
|
|
19
43
|
export declare function main(argv?: string[]): Promise<number>;
|
package/dist/cli.js
CHANGED
|
@@ -918,9 +918,86 @@ async function cmdDoctor(a) {
|
|
|
918
918
|
allOk = allOk && ok;
|
|
919
919
|
console.log(` ${ok ? OK : BAD} ${name.padEnd(20)} ${detail}`);
|
|
920
920
|
}
|
|
921
|
+
// Verified-tier readiness. Separate from the checks above because none of these is a failure of
|
|
922
|
+
// the agent: it will run, it just will not be RANKED. Conflating the two trains people to ignore
|
|
923
|
+
// a red mark that sometimes means nothing.
|
|
924
|
+
//
|
|
925
|
+
// Mirrors the Python SDK's section deliberately, down to the wording, because a developer must
|
|
926
|
+
// not get a different answer about their own eligibility depending on which SDK they installed.
|
|
927
|
+
await printVerifiedReadiness(base, c, cfg);
|
|
921
928
|
console.log("\n" + (allOk ? "✓ ready — `pyyol dev` to practice, `pyyol play <arena>` to compete." : "fix the ✗ items above."));
|
|
922
929
|
return allOk ? 0 : 1;
|
|
923
930
|
}
|
|
931
|
+
/** True if the agent's entry file appears to send a system prompt, false if not, null if unknown.
|
|
932
|
+
*
|
|
933
|
+
* A shallow source scan, deliberately: it recognises Anthropic's `system=` and OpenAI's system /
|
|
934
|
+
* developer roles. Being approximate is fine because a wrong answer here is a HINT, not a
|
|
935
|
+
* decision — the authoritative value is the `scaffold` field on a real decision, which is what the
|
|
936
|
+
* unknown case points at.
|
|
937
|
+
*
|
|
938
|
+
* "developer" is included because it is OpenAI's newer name for the system role, and missing it
|
|
939
|
+
* would tell a correctly built agent it is ineligible — worse than saying nothing. */
|
|
940
|
+
export async function scaffoldHint(entry) {
|
|
941
|
+
if (!entry)
|
|
942
|
+
return null;
|
|
943
|
+
const path = String(entry).split(":")[0];
|
|
944
|
+
try {
|
|
945
|
+
const { readFileSync } = await import("node:fs");
|
|
946
|
+
const src = readFileSync(path, "utf8");
|
|
947
|
+
return ["system:", "system =", '"system"', "'system'", '"developer"', "'developer'"].some((n) => src.includes(n));
|
|
948
|
+
}
|
|
949
|
+
catch {
|
|
950
|
+
return null;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
/** Report whether this agent will actually earn Verified, and if not, exactly why.
|
|
954
|
+
*
|
|
955
|
+
* Three things decide it and each fails silently on its own: routing (without it the platform sees
|
|
956
|
+
* no calls at all), a system prompt (without one the harness cannot be fingerprinted, so the agent
|
|
957
|
+
* is excluded from paired model comparison), and coverage (the share of decisions actually proven,
|
|
958
|
+
* against the threshold the verified tier requires).
|
|
959
|
+
*
|
|
960
|
+
* An agent can run perfectly and earn nothing. Nothing else in the toolchain says so, and learning
|
|
961
|
+
* it from an empty leaderboard row weeks later is the failure this prevents. */
|
|
962
|
+
export async function printVerifiedReadiness(base, c, cfg) {
|
|
963
|
+
const { gatewayBaseUrl } = await import("./instrument.js");
|
|
964
|
+
const { explain, ISSUE_NO_SYSTEM_PROMPT } = await import("./scaffold.js");
|
|
965
|
+
console.log("\nverified tier");
|
|
966
|
+
const routed = Boolean(gatewayBaseUrl("anthropic") || gatewayBaseUrl("openai"));
|
|
967
|
+
console.log(` ${routed ? OK : WARN} ${"gateway routing".padEnd(20)} ` +
|
|
968
|
+
(routed
|
|
969
|
+
? "on — model calls are server-observed"
|
|
970
|
+
: "off — call pyyol.route(client) after pyyol.instrument(); without it no decision can be " +
|
|
971
|
+
"proven and this agent cannot appear on the model board"));
|
|
972
|
+
const hint = await scaffoldHint(cfg?.entry);
|
|
973
|
+
if (hint === null) {
|
|
974
|
+
console.log(` ${WARN} ${"system prompt".padEnd(20)} could not inspect the agent source; run \`pyyol dev\` ` +
|
|
975
|
+
"and check `scaffold` on a decision in the trace");
|
|
976
|
+
}
|
|
977
|
+
else if (hint) {
|
|
978
|
+
console.log(` ${OK} ${"system prompt".padEnd(20)} found — the harness can be fingerprinted, so this ` +
|
|
979
|
+
"agent is eligible for paired model comparison");
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
// The shared explanation, never a paraphrase: the SDKs, the trace and this command have to give
|
|
983
|
+
// a developer the same sentence about one rule.
|
|
984
|
+
console.log(` ${WARN} ${"system prompt".padEnd(20)} none found. ${explain(ISSUE_NO_SYSTEM_PROMPT)}`);
|
|
985
|
+
}
|
|
986
|
+
if (base && c?.accessToken) {
|
|
987
|
+
const [st, body] = await apiGet(`${base}/v1/gw/coverage`, c.accessToken);
|
|
988
|
+
const b = body;
|
|
989
|
+
if (st === 200 && b?.decisions) {
|
|
990
|
+
const cov = Number(b.coverage ?? 0);
|
|
991
|
+
const mark = cov >= 0.9 ? OK : WARN;
|
|
992
|
+
console.log(` ${mark} ${"coverage".padEnd(20)} ${b.bound_decisions ?? 0}/${b.decisions} decisions ` +
|
|
993
|
+
`proven (${(cov * 100).toFixed(1)}%)` +
|
|
994
|
+
(cov >= 0.9 ? "" : " — below the 90% the verified tier requires"));
|
|
995
|
+
}
|
|
996
|
+
else if (st === 200) {
|
|
997
|
+
console.log(` ${WARN} ${"coverage".padEnd(20)} no decisions recorded yet — play a match first`);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
924
1001
|
async function cmdUpdate() {
|
|
925
1002
|
console.log(`pyyol ${SDK_VERSION}`);
|
|
926
1003
|
try {
|
package/dist/index.d.ts
CHANGED
|
@@ -27,4 +27,6 @@ export { instrument, uninstrument, recordResponse, extractUsage, patchPrototype
|
|
|
27
27
|
export { route, enableGateway, disableGateway, gatewayBaseUrl, gatewayHeaders } from "./instrument.js";
|
|
28
28
|
export type { ExtractedUsage } from "./instrument.js";
|
|
29
29
|
export { estimateCost, rateFor, isKnown, canonical, PRICING_VERSION } from "./pricing.js";
|
|
30
|
+
export { moveTool, moveToolChoice, moveToolName, moveFromResponse, boundMove, boundPlan, canonPlan, PLAN_KEY, MAX_SPAN_ROUNDS, canonMove, canonGoofspiel, canonMafia, canonMonopoly, NO_TARGET, TOOL_GOOFSPIEL, TOOL_MAFIA, TOOL_MONOPOLY, GAME_GOOFSPIEL, GAME_MAFIA, GAME_MONOPOLY, } from "./movetools.js";
|
|
30
31
|
export type { Rate, CostArgs } from "./pricing.js";
|
|
32
|
+
export { fingerprint as scaffoldFingerprint, fromRequest as scaffoldFromRequest, eligibleForPairing as scaffoldEligibleForPairing, SCAFFOLD_VERSION, } from "./scaffold.js";
|
package/dist/index.js
CHANGED
|
@@ -21,3 +21,14 @@ export { Tracer, Span, currentSpan, matchTraceId, currentUsage, UsageAccumulator
|
|
|
21
21
|
export { instrument, uninstrument, recordResponse, extractUsage, patchPrototype } from "./instrument.js";
|
|
22
22
|
export { route, enableGateway, disableGateway, gatewayBaseUrl, gatewayHeaders } from "./instrument.js";
|
|
23
23
|
export { estimateCost, rateFor, isKnown, canonical, PRICING_VERSION } from "./pricing.js";
|
|
24
|
+
// Structured move tools: how an agent proves its MODEL chose the move it played. Routing
|
|
25
|
+
// through the gateway proves a call happened for a turn; a tool call is what proves the
|
|
26
|
+
// model's answer became the move. See src/movetools.ts.
|
|
27
|
+
export { moveTool, moveToolChoice, moveToolName, moveFromResponse, boundMove,
|
|
28
|
+
// Range bindings: one completion that decided several rounds. Coverage counts DECISIONS a
|
|
29
|
+
// model made, not calls, so batching no longer costs an agent its verified share.
|
|
30
|
+
boundPlan, canonPlan, PLAN_KEY, MAX_SPAN_ROUNDS, canonMove, canonGoofspiel, canonMafia, canonMonopoly, NO_TARGET, TOOL_GOOFSPIEL, TOOL_MAFIA, TOOL_MONOPOLY, GAME_GOOFSPIEL, GAME_MAFIA, GAME_MONOPOLY, } from "./movetools.js";
|
|
31
|
+
// Scaffold fingerprinting: the harness identity that makes a paired model comparison
|
|
32
|
+
// possible (same scaffold, different model). Exported so a developer can print their own
|
|
33
|
+
// fingerprint and confirm it is stable before relying on it.
|
|
34
|
+
export { fingerprint as scaffoldFingerprint, fromRequest as scaffoldFromRequest, eligibleForPairing as scaffoldEligibleForPairing, SCAFFOLD_VERSION, } from "./scaffold.js";
|
package/dist/instrument.d.ts
CHANGED
|
@@ -2,7 +2,15 @@ type Any = any;
|
|
|
2
2
|
/** Enable gateway routing (called by the runtime in ranked mode; safe in tests). */
|
|
3
3
|
export declare function enableGateway(agentKey: string, baseUrl: string): void;
|
|
4
4
|
export declare function disableGateway(): void;
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* The baseURL a provider client should point at, or "" when routing is off or the provider
|
|
7
|
+
* cannot be routed.
|
|
8
|
+
*
|
|
9
|
+
* Returns "" for a LOCAL/self-hosted provider, deliberately: the gateway runs on Pyyol's side
|
|
10
|
+
* and cannot reach a model server on the developer's own machine, so pointing a client at it
|
|
11
|
+
* would break every call. That play is unverified — and also free, so no cost attribution is
|
|
12
|
+
* lost either.
|
|
13
|
+
*/
|
|
6
14
|
export declare function gatewayBaseUrl(provider: string): string;
|
|
7
15
|
/** The X-Pyyol-* identity headers for the current turn (empty if routing off). */
|
|
8
16
|
export declare function gatewayHeaders(): Record<string, string>;
|
|
@@ -11,7 +19,8 @@ export declare function gatewayHeaders(): Record<string, string>;
|
|
|
11
19
|
* `patchedAs` is the SDK we wrapped (which wire format this is). The client's baseURL
|
|
12
20
|
* is consulted first and wins. A baseURL pointing at the PYYOL GATEWAY is not used for
|
|
13
21
|
* attribution — it says the call was proxied, not who served it — so the upstream is
|
|
14
|
-
* recovered
|
|
22
|
+
* recovered by PARSING the gateway path (/gw/<slug>[/v1]) rather than matching a table, so a
|
|
23
|
+
* provider added tomorrow is attributed correctly without touching this. */
|
|
15
24
|
export declare function resolveCallProvider(resource: Any, patchedAs: string): string;
|
|
16
25
|
/** Point a provider client at the Pyyol Gateway (sets its baseURL). Explicit, robust
|
|
17
26
|
* opt-in that operates on the given instance. Returns the client. No-op when routing
|
|
@@ -22,9 +31,26 @@ export interface ExtractedUsage {
|
|
|
22
31
|
provider: string;
|
|
23
32
|
promptTokens: number;
|
|
24
33
|
completionTokens: number;
|
|
34
|
+
/** Prompt-cache READ tokens. */
|
|
25
35
|
cachedTokens: number;
|
|
36
|
+
/** Prompt-cache WRITE/creation tokens. Billed at 1.25x input on Anthropic, so an
|
|
37
|
+
* agent's most expensive tokens were previously recorded as zero. */
|
|
38
|
+
cachedWriteTokens: number;
|
|
26
39
|
reasoningTokens: number;
|
|
27
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Pull normalized usage from ANY provider response, or null if it carries none.
|
|
43
|
+
*
|
|
44
|
+
* Normalizes onto ONE convention: promptTokens is the total billable input, with cache reads and
|
|
45
|
+
* writes as SUBSETS of it. Providers genuinely disagree and the disagreement is silent, so the rule
|
|
46
|
+
* follows the WORD USED rather than a vendor list:
|
|
47
|
+
*
|
|
48
|
+
* - a PROMPT-family key names the whole prompt, so cache is already inside it
|
|
49
|
+
* - an INPUT-family key names fresh input, so cache is billed on top
|
|
50
|
+
*
|
|
51
|
+
* A reported total cross-checks the additive case, so a provider using "input" for a
|
|
52
|
+
* cache-inclusive total is corrected by its own arithmetic instead of being over-counted.
|
|
53
|
+
*/
|
|
28
54
|
export declare function extractUsage(resp: Any): ExtractedUsage | null;
|
|
29
55
|
/** Record usage from a provider response: compute cost, add to the turn
|
|
30
56
|
* accumulator, and emit a Lens model_call span. Returns the extracted usage (or
|
|
@@ -35,7 +61,7 @@ export declare function recordResponse(resp: Any, o?: {
|
|
|
35
61
|
}): ExtractedUsage | null;
|
|
36
62
|
/** @internal Wrap `proto[method]` so its resolved return value is recorded.
|
|
37
63
|
* Idempotent and fully guarded. Exported for tests. */
|
|
38
|
-
export declare function patchPrototype(proto: Any, method: string, provider: string): boolean;
|
|
64
|
+
export declare function patchPrototype(proto: Any, method: string, provider: string, endpoint?: string): boolean;
|
|
39
65
|
/** Auto-capture LLM usage from installed providers. Pass e.g. `["openai"]` to limit
|
|
40
66
|
* which are patched; default patches all supported providers that are installed.
|
|
41
67
|
* Returns the list actually instrumented. Safe to call more than once. */
|