pi-daddy 0.32.0 → 0.34.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 (139) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/contracts/ledger-record/v1/governance-event.schema.json +46 -1
  3. package/dist/advisors/advisor.d.ts +49 -0
  4. package/dist/advisors/advisor.d.ts.map +1 -0
  5. package/dist/advisors/advisor.js +76 -0
  6. package/dist/advisors/advisor.js.map +1 -0
  7. package/dist/advisors/decider.d.ts +75 -0
  8. package/dist/advisors/decider.d.ts.map +1 -0
  9. package/dist/advisors/decider.js +29 -0
  10. package/dist/advisors/decider.js.map +1 -0
  11. package/dist/advisors/jev.d.ts +41 -0
  12. package/dist/advisors/jev.d.ts.map +1 -0
  13. package/dist/advisors/jev.js +108 -0
  14. package/dist/advisors/jev.js.map +1 -0
  15. package/dist/advisors/settings.d.ts +38 -0
  16. package/dist/advisors/settings.d.ts.map +1 -0
  17. package/dist/advisors/settings.js +60 -0
  18. package/dist/advisors/settings.js.map +1 -0
  19. package/dist/cli.d.ts +4 -0
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +12 -3
  22. package/dist/cli.js.map +1 -1
  23. package/dist/executors/activity-session.d.ts.map +1 -1
  24. package/dist/executors/activity-session.js +27 -0
  25. package/dist/executors/activity-session.js.map +1 -1
  26. package/dist/executors/herdr-cli.d.ts +1 -1
  27. package/dist/executors/herdr-cli.js +1 -1
  28. package/dist/executors/herdr-poll.d.ts +1 -1
  29. package/dist/executors/herdr-poll.js +1 -1
  30. package/dist/executors/herdr-stage.d.ts +1 -1
  31. package/dist/executors/herdr-stage.d.ts.map +1 -1
  32. package/dist/executors/herdr-stage.js +25 -8
  33. package/dist/executors/herdr-stage.js.map +1 -1
  34. package/dist/executors/pane-reaper.d.ts +1 -1
  35. package/dist/executors/pane-reaper.js +2 -2
  36. package/dist/executors/pane-reaper.js.map +1 -1
  37. package/dist/executors/run-herdr.d.ts +3 -3
  38. package/dist/executors/run-herdr.js +2 -2
  39. package/dist/governance/ledger-report.d.ts +1 -1
  40. package/dist/governance/ledger-v3-validation.d.ts.map +1 -1
  41. package/dist/governance/ledger-v3-validation.js +1 -0
  42. package/dist/governance/ledger-v3-validation.js.map +1 -1
  43. package/dist/governance/ledger.d.ts +15 -1
  44. package/dist/governance/ledger.d.ts.map +1 -1
  45. package/dist/governance/ledger.js +2 -1
  46. package/dist/governance/ledger.js.map +1 -1
  47. package/dist/governance/workspace-lease.js +1 -1
  48. package/dist/kernel/capabilities.d.ts +3 -3
  49. package/dist/kernel/capabilities.d.ts.map +1 -1
  50. package/dist/kernel/capabilities.js +7 -3
  51. package/dist/kernel/capabilities.js.map +1 -1
  52. package/dist/kernel/catalog.d.ts +27 -0
  53. package/dist/kernel/catalog.d.ts.map +1 -1
  54. package/dist/kernel/catalog.js +65 -1
  55. package/dist/kernel/catalog.js.map +1 -1
  56. package/dist/kernel/chain.d.ts +2 -0
  57. package/dist/kernel/chain.d.ts.map +1 -1
  58. package/dist/kernel/chain.js.map +1 -1
  59. package/dist/kernel/context-handoff.d.ts +85 -0
  60. package/dist/kernel/context-handoff.d.ts.map +1 -0
  61. package/dist/kernel/context-handoff.js +177 -0
  62. package/dist/kernel/context-handoff.js.map +1 -0
  63. package/dist/kernel/delegate-types.d.ts +60 -0
  64. package/dist/kernel/delegate-types.d.ts.map +1 -1
  65. package/dist/kernel/delegate-types.js.map +1 -1
  66. package/dist/kernel/delegate.d.ts.map +1 -1
  67. package/dist/kernel/delegate.js +55 -3
  68. package/dist/kernel/delegate.js.map +1 -1
  69. package/dist/kernel/env-names.d.ts +10 -0
  70. package/dist/kernel/env-names.d.ts.map +1 -1
  71. package/dist/kernel/env-names.js +11 -0
  72. package/dist/kernel/env-names.js.map +1 -1
  73. package/dist/kernel/grant-env.d.ts +1 -1
  74. package/dist/kernel/grant-env.js +1 -1
  75. package/dist/kernel/propagation.d.ts +2 -2
  76. package/dist/kernel/propagation.d.ts.map +1 -1
  77. package/dist/kernel/propagation.js +10 -3
  78. package/dist/kernel/propagation.js.map +1 -1
  79. package/dist/kernel/refusals.d.ts +1 -1
  80. package/dist/kernel/refusals.d.ts.map +1 -1
  81. package/dist/kernel/refusals.js +1 -0
  82. package/dist/kernel/refusals.js.map +1 -1
  83. package/dist/kernel/resolve.d.ts +2 -2
  84. package/dist/kernel/resolve.d.ts.map +1 -1
  85. package/dist/kernel/resolve.js +7 -3
  86. package/dist/kernel/resolve.js.map +1 -1
  87. package/dist/kernel/routing-authority.d.ts +1 -1
  88. package/dist/kernel/routing-authority.js +1 -1
  89. package/dist/kernel/skill-packages.d.ts +1 -1
  90. package/dist/kernel/skill-packages.js +1 -1
  91. package/dist/kernel/spawn.d.ts +22 -1
  92. package/dist/kernel/spawn.d.ts.map +1 -1
  93. package/dist/kernel/spawn.js +11 -4
  94. package/dist/kernel/spawn.js.map +1 -1
  95. package/dist/kernel/workspace.d.ts +1 -1
  96. package/extensions/advisor-session.ts +64 -0
  97. package/extensions/chain-plan.ts +7 -1
  98. package/extensions/context-shape.ts +30 -0
  99. package/extensions/context-staging.ts +200 -0
  100. package/extensions/delegate-chain.ts +2 -0
  101. package/extensions/delegation-ledger.ts +2 -0
  102. package/extensions/delegation.ts +4 -0
  103. package/extensions/execute-child.ts +9 -1
  104. package/extensions/grants-command.ts +1 -1
  105. package/extensions/grants.ts +5 -0
  106. package/extensions/run-delegation.ts +3 -0
  107. package/extensions/session-report.ts +1 -1
  108. package/extensions/session.ts +15 -0
  109. package/package.json +1 -1
  110. package/src/advisors/advisor.ts +123 -0
  111. package/src/advisors/decider.ts +64 -0
  112. package/src/advisors/jev.ts +130 -0
  113. package/src/advisors/settings.ts +73 -0
  114. package/src/cli.ts +13 -3
  115. package/src/executors/activity-session.ts +28 -0
  116. package/src/executors/herdr-cli.ts +1 -1
  117. package/src/executors/herdr-poll.ts +1 -1
  118. package/src/executors/herdr-stage.ts +26 -8
  119. package/src/executors/pane-reaper.ts +2 -2
  120. package/src/executors/run-herdr.ts +4 -4
  121. package/src/governance/ledger-report.ts +1 -1
  122. package/src/governance/ledger-v3-validation.ts +1 -0
  123. package/src/governance/ledger.ts +16 -1
  124. package/src/governance/workspace-lease.ts +1 -1
  125. package/src/kernel/capabilities.ts +7 -3
  126. package/src/kernel/catalog.ts +67 -1
  127. package/src/kernel/chain.ts +2 -0
  128. package/src/kernel/context-handoff.ts +231 -0
  129. package/src/kernel/delegate-types.ts +51 -0
  130. package/src/kernel/delegate.ts +60 -3
  131. package/src/kernel/env-names.ts +11 -0
  132. package/src/kernel/grant-env.ts +1 -1
  133. package/src/kernel/propagation.ts +10 -2
  134. package/src/kernel/refusals.ts +1 -0
  135. package/src/kernel/resolve.ts +7 -3
  136. package/src/kernel/routing-authority.ts +1 -1
  137. package/src/kernel/skill-packages.ts +1 -1
  138. package/src/kernel/spawn.ts +34 -4
  139. package/src/kernel/workspace.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -12,6 +12,63 @@ the record of how the package got here and are worth keeping; they are not worth
12
12
  > the record of how the package arrived at what it does, and because the reasoning behind each one is
13
13
  > usually the clearest statement of why the current behaviour is what it is.
14
14
 
15
+ ## 0.34.0 — an advisors layer, off by default (ADR-0077)
16
+
17
+ `src/advisors/` holds a `Decider` that answers typed questions — `noul` (a boolean), `choice` (one of the options
18
+ the caller already had) and `score` (a level from the caller's own list) — each with a probability. It may select,
19
+ rank, annotate or propose, and it can never widen a grant, satisfy a gate or replace a human's answer. That is
20
+ enforced rather than promised: no type in the layer names a capability or a refusal code, and no kernel or
21
+ governance module imports it, both checked by tests.
22
+
23
+ Off by default. An advisor runs only when `.pi/pi-daddy/settings.json` has an `advisor` block saying so and
24
+ `PI_DADDY_ADVISOR_KEY` is set; malformed configuration disables it and names the field rather than failing either
25
+ silently or open. Every use writes an `advice` record to the ledger, including the uses that produced nothing, and
26
+ that record never contains the state the caller composed. Degradation is always "no advice": disabled, no key, a
27
+ two-second timeout, a transport error or an unrecognised response all return the same nothing, so a caller written
28
+ against the null decider behaves identically with an advisor present.
29
+
30
+ The first adapter is TypeSafe's Jev through OpenRouter's Decisions endpoint (`typesafe/jev-1.13`). The request shape
31
+ is the documented one. The response shape is **not confirmed against a live call** — OpenRouter describes the
32
+ `answers` object without showing it — so the parser accepts what the documentation describes and treats anything
33
+ else, including a choice that was never offered, as no advice. No decision point uses an advisor yet.
34
+
35
+ There is no dashboard toggle: the dashboard is a read-only renderer that never affects enforcement, so enabling an
36
+ advisor is an operator edit to the reviewable settings file.
37
+
38
+ ## 0.33.0 — a child can be given context, and the giving attenuates (ADR-0078)
39
+
40
+ `delegate`, `delegate_all` and each fan-out child accept a `context` parameter naming how much of the parent's own
41
+ session crosses: `none` (the default, and what every child got before), `files`, `pruned`, `summary` or `fork`. Each
42
+ mode is a `context:<mode>` capability in a new namespace, so it is intersected with the parent's grant and the
43
+ definition's `allowed-tools` ceiling exactly like a tool, appears in `/grants` and in the ledger's effective set, and
44
+ cannot be widened by a child. The modes are ordered and each subsumes the weaker ones.
45
+
46
+ `context:fork` is **gated by default** beside `tool:bash`: it is the one mode that can carry content an untrusted
47
+ repository put in front of the parent into a fresh child. What crosses arrives in a `<<<PARENT-CONTEXT …>>>` fence
48
+ marked as data, distinct from the chain handoff's fence, capped at 32 KiB with anything dropped said inside the
49
+ fence. Paths named for `files` are confined to the session's working directory. The capability-decision record gains
50
+ `handoff`, naming the mode the child received, how many sections and bytes crossed, and for `pruned` which rule ran
51
+ and how many turns it kept.
52
+
53
+ Nothing changes for a caller that passes no `context`: the default is `none`, which adds no capability and crosses
54
+ nothing. `pruned`'s selection rule is deterministic but its recall is unmeasured, so it is not a default.
55
+
56
+ A `delegate_chain` step takes the same parameter. A chain is planned as one unit, so a step's handoff is capped by
57
+ that step's own definition and any gate it raises is answered before the first step runs.
58
+
59
+ **Breaking for consumers of the package API:** `splitSystemPrompt` now returns `systemPrompts: string[]` instead of
60
+ `systemPrompt?: string`, because a child can carry more than one appended system prompt and only the first was being
61
+ staged for the Herdr executor.
62
+
63
+ ## 0.32.1 — a doubled namespace in `allowed-tools` is explained, not just reported
64
+
65
+ An `allowed-tools` entry written with a capitalised namespace, such as `Tool:Read`, misses the lower-case prefix test,
66
+ is prefixed a second time on the bare-entry path and becomes `tool:tool:read`; `Workspace:prod` becomes
67
+ `tool:workspace:prod`. That spelling is the one the Agent Skills standard and this package's own README example
68
+ invite. `pi-daddy init` then refuses the definition and named only the mangled identifier, and a spawn refusal said
69
+ the capability was absent from the catalog. Both messages now name the mistake and the entry that was meant. No
70
+ behaviour beyond the wording changes: the refusal, its code and the effective grant are exactly as before.
71
+
15
72
  ## 0.32.0 — a child is stopped for inactivity, not by a wall clock (ADR-0076 PR 3e)
16
73
 
17
74
  A governed child is now stopped when `PI_DADDY_CHILD_IDLE_TIMEOUT` seconds pass with no activity (default fifteen
@@ -185,7 +185,8 @@
185
185
  "WORKSPACE_NOT_AUTHORIZED",
186
186
  "WORKSPACE_WRITE_CONFLICT",
187
187
  "WORKSPACE_LEASE_STALE",
188
- "LEDGER_DAMAGED"
188
+ "LEDGER_DAMAGED",
189
+ "CONTEXT_REQUEST_INVALID"
189
190
  ]
190
191
  },
191
192
  "refusal": {
@@ -401,6 +402,50 @@
401
402
  "definitionDigest": {
402
403
  "$ref": "#/$defs/definitionDigest"
403
404
  },
405
+ "handoff": {
406
+ "type": "object",
407
+ "description": "the context handoff this child received (ADR-0078); absent means nothing crossed",
408
+ "additionalProperties": false,
409
+ "required": [
410
+ "mode",
411
+ "sections",
412
+ "bytes",
413
+ "truncatedBytes"
414
+ ],
415
+ "properties": {
416
+ "mode": {
417
+ "enum": [
418
+ "files",
419
+ "pruned",
420
+ "summary",
421
+ "fork"
422
+ ]
423
+ },
424
+ "sections": {
425
+ "type": "integer",
426
+ "minimum": 0
427
+ },
428
+ "bytes": {
429
+ "type": "integer",
430
+ "minimum": 0
431
+ },
432
+ "truncatedBytes": {
433
+ "type": "integer",
434
+ "minimum": 0
435
+ },
436
+ "keptTurns": {
437
+ "type": "integer",
438
+ "minimum": 0
439
+ },
440
+ "droppedTurns": {
441
+ "type": "integer",
442
+ "minimum": 0
443
+ },
444
+ "rule": {
445
+ "type": "string"
446
+ }
447
+ }
448
+ },
404
449
  "executor": {
405
450
  "type": "string",
406
451
  "enum": [
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The wrapper that makes "every use is recorded" structural rather than a rule (ADR-0077).
3
+ *
4
+ * A caller cannot reach a `Decider` directly through this layer's public surface: it asks an `Advisor`, and asking
5
+ * always produces an `advice` record — including when the answer was "no advice", which is the case a reviewer most
6
+ * needs to see, because an advisor that silently stops answering would otherwise look exactly like one nobody used.
7
+ *
8
+ * **What is recorded, and what is not.** The record names the purpose, the decider, the question keys, the answers
9
+ * and how long it took. It does NOT contain the state. A caller composes that state from its own context, which can
10
+ * include task text, file contents and a repository's private material; the ledger has never stored a task
11
+ * (ADR-0021) and an advisor must not become the way it starts. The question keys are the caller's own constants,
12
+ * so they name the decision without describing the situation.
13
+ */
14
+ import type { Advice, AdviceRequest, Decider } from "./decider.ts";
15
+ /** Two seconds. An advisor is on the path of a decision a human is waiting for; it is not worth more than that. */
16
+ export declare const DEFAULT_ADVICE_TIMEOUT_MS = 2000;
17
+ export interface AdviceRecord {
18
+ /** Which decision this advice was for, from the caller's own closed list. */
19
+ purpose: string;
20
+ decider: string;
21
+ /** Question keys only — never the state, and never a question's free text. */
22
+ questions: string[];
23
+ answered: boolean;
24
+ durationMs: number;
25
+ /** Present only when advice came back. */
26
+ answers?: Readonly<Record<string, {
27
+ value: string | number | boolean;
28
+ confidence?: number;
29
+ }>>;
30
+ model?: string;
31
+ /**
32
+ * Why there is no advice. `declined` means the advisor answered with nothing; `error` means it could not be
33
+ * reached or its response was unrecognised; `cancelled` means the CALLER went away, which is not the advisor's
34
+ * failure and must not read as one.
35
+ */
36
+ outcome: "answered" | "disabled" | "timeout" | "error" | "declined" | "cancelled";
37
+ }
38
+ export interface Advisor {
39
+ ask(purpose: string, request: AdviceRequest, signal?: AbortSignal): Promise<Advice | null>;
40
+ }
41
+ export declare function createAdvisor(input: {
42
+ decider: Decider;
43
+ /** Where the record goes. Injected so this layer does no I/O and governance does not import it. */
44
+ record: (entry: AdviceRecord) => void | Promise<void>;
45
+ timeoutMs?: number;
46
+ /** Absent or false means the null decider is used whatever `decider` says. */
47
+ enabled?: boolean;
48
+ }): Advisor;
49
+ //# sourceMappingURL=advisor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advisor.d.ts","sourceRoot":"","sources":["../../src/advisors/advisor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEnE,mHAAmH;AACnH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;CACnF;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC5F;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,mGAAmG;IACnG,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CA+DV"}
@@ -0,0 +1,76 @@
1
+ /** Two seconds. An advisor is on the path of a decision a human is waiting for; it is not worth more than that. */
2
+ export const DEFAULT_ADVICE_TIMEOUT_MS = 2000;
3
+ export function createAdvisor(input) {
4
+ const timeoutMs = input.timeoutMs ?? DEFAULT_ADVICE_TIMEOUT_MS;
5
+ return {
6
+ async ask(purpose, request, signal) {
7
+ const started = Date.now();
8
+ const base = { purpose, decider: input.decider.name, questions: Object.keys(request.questions) };
9
+ const write = async (entry) => {
10
+ try {
11
+ await input.record(entry);
12
+ }
13
+ catch {
14
+ // Recording is an observation of a decision that has already been taken. Failing to write it must not
15
+ // change what the caller does, for `execute-child`'s reason: an audit failure that discards the work is
16
+ // worse than one that is merely missing.
17
+ }
18
+ };
19
+ if (input.enabled !== true) {
20
+ await write({ ...base, decider: "none", answered: false, durationMs: 0, outcome: "disabled" });
21
+ return null;
22
+ }
23
+ const timer = new AbortController();
24
+ const cancel = setTimeout(() => timer.abort(), timeoutMs);
25
+ const linked = signal ? AbortSignal.any([signal, timer.signal]) : timer.signal;
26
+ try {
27
+ // RACED, not merely signalled. A decider that ignores its signal would otherwise run as long as it liked
28
+ // and then be recorded as a timeout — measured at fifty times the configured bound. The `Decider` contract
29
+ // cannot make an implementation honour an abort, so the bound is enforced on this side of it.
30
+ const advice = await Promise.race([
31
+ input.decider.decide(request, linked),
32
+ new Promise((settle) => linked.addEventListener("abort", () => settle(null), { once: true })),
33
+ ]);
34
+ const durationMs = Date.now() - started;
35
+ if (!advice) {
36
+ await write({ ...base, answered: false, durationMs, outcome: outcomeFor(timer.signal, signal, "declined") });
37
+ return null;
38
+ }
39
+ await write({
40
+ ...base,
41
+ answered: true,
42
+ durationMs,
43
+ outcome: "answered",
44
+ answers: Object.fromEntries(Object.entries(advice.answers).map(([key, answer]) => [
45
+ key,
46
+ { value: answer.value, ...(answer.confidence === undefined ? {} : { confidence: answer.confidence }) },
47
+ ])),
48
+ ...(advice.model ? { model: advice.model } : {}),
49
+ });
50
+ return advice;
51
+ }
52
+ catch {
53
+ // Including an abort. A caller asked for advice and is getting none; it proceeds exactly as it would have.
54
+ await write({
55
+ ...base,
56
+ answered: false,
57
+ durationMs: Date.now() - started,
58
+ outcome: outcomeFor(timer.signal, signal, "error"),
59
+ });
60
+ return null;
61
+ }
62
+ finally {
63
+ clearTimeout(cancel);
64
+ }
65
+ },
66
+ };
67
+ }
68
+ /** The bound fired, the caller went away, or neither — three different facts a reviewer needs to tell apart. */
69
+ function outcomeFor(timer, caller, otherwise) {
70
+ if (timer.aborted && !caller?.aborted)
71
+ return "timeout";
72
+ if (caller?.aborted)
73
+ return "cancelled";
74
+ return otherwise;
75
+ }
76
+ //# sourceMappingURL=advisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advisor.js","sourceRoot":"","sources":["../../src/advisors/advisor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * An advisor is a thing that answers a typed question. It is never a thing that decides (ADR-0077).
3
+ *
4
+ * The boundary is the whole design, and it is structural rather than remembered: **no type in this layer names a
5
+ * `Capability` or a refusal code**, and no function in `kernel/` or `governance/` accepts an advisor's result. An
6
+ * advisor may select among options the caller already had, rank them, annotate them, or propose one. It can never
7
+ * widen an `effective` set, satisfy a gate, or stand in for a human's answer — not because it is asked not to, but
8
+ * because nothing on those paths can receive what it returns. `test/advisors.test.ts` forces that.
9
+ *
10
+ * Non-generative on purpose. The first advisor is a classifier that returns a typed answer with a probability, not
11
+ * prose, which is what makes an advisor auditable: "it chose `b` at 0.91" is a fact a reviewer can disagree with,
12
+ * where a paragraph of reasoning is not.
13
+ *
14
+ * Degradation is "no advice", never a guess. Every path that cannot produce an answer — disabled, missing key,
15
+ * timeout, transport error, a response shape we do not recognise — returns `null`, and the caller does what it
16
+ * would have done without an advisor at all. That is why a caller must be written to work with `nullDecider`
17
+ * first, and why `nullDecider` is the default.
18
+ */
19
+ /** A question, in the three shapes the first advisor understands. */
20
+ export type Question = {
21
+ kind: "noul";
22
+ instructions: string;
23
+ whenTrue: string;
24
+ whenFalse: string;
25
+ } | {
26
+ kind: "choice";
27
+ instructions: string;
28
+ options: Readonly<Record<string, string>>;
29
+ } | {
30
+ kind: "score";
31
+ instructions: string;
32
+ levels: readonly string[];
33
+ };
34
+ /** One typed answer. `confidence` is absent when the transport did not report one; it is never invented. */
35
+ export type Answer = {
36
+ kind: "noul";
37
+ value: boolean;
38
+ confidence?: number;
39
+ } | {
40
+ kind: "choice";
41
+ value: string;
42
+ confidence?: number;
43
+ } | {
44
+ kind: "score";
45
+ value: number;
46
+ level: string;
47
+ confidence?: number;
48
+ };
49
+ export interface AdviceRequest {
50
+ /**
51
+ * What the advisor is told about the situation. **Caller-composed and deliberately not the raw task**: the task
52
+ * is never stored (ADR-0021) and must not be shipped to a third party either, so a caller passes the facts it
53
+ * chose, and the record below names them by key without their values.
54
+ */
55
+ state: Readonly<Record<string, unknown>>;
56
+ questions: Readonly<Record<string, Question>>;
57
+ }
58
+ export interface Advice {
59
+ answers: Readonly<Record<string, Answer>>;
60
+ /** What actually answered, as the transport reported it — a dated model id, not the one we asked for. */
61
+ model?: string;
62
+ }
63
+ export interface Decider {
64
+ /** Recorded in the ledger so a reviewer can tell which advisor a decision was taken beside. */
65
+ readonly name: string;
66
+ decide(request: AdviceRequest, signal?: AbortSignal): Promise<Advice | null>;
67
+ }
68
+ /**
69
+ * The default, and the one every caller must work correctly with.
70
+ *
71
+ * Not a placeholder: it is how advisors stay optional. A caller that behaves differently under `nullDecider` than
72
+ * under no advisor at all has made advice load-bearing, which is the one thing ADR-0077 forbids.
73
+ */
74
+ export declare const nullDecider: Decider;
75
+ //# sourceMappingURL=decider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decider.d.ts","sourceRoot":"","sources":["../../src/advisors/decider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,qEAAqE;AACrE,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC3E;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,GACnF;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAEvE,4GAA4G;AAC5G,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAEtD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1C,yGAAyG;IACzG,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,+FAA+F;IAC/F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9E;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,OAGzB,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * An advisor is a thing that answers a typed question. It is never a thing that decides (ADR-0077).
3
+ *
4
+ * The boundary is the whole design, and it is structural rather than remembered: **no type in this layer names a
5
+ * `Capability` or a refusal code**, and no function in `kernel/` or `governance/` accepts an advisor's result. An
6
+ * advisor may select among options the caller already had, rank them, annotate them, or propose one. It can never
7
+ * widen an `effective` set, satisfy a gate, or stand in for a human's answer — not because it is asked not to, but
8
+ * because nothing on those paths can receive what it returns. `test/advisors.test.ts` forces that.
9
+ *
10
+ * Non-generative on purpose. The first advisor is a classifier that returns a typed answer with a probability, not
11
+ * prose, which is what makes an advisor auditable: "it chose `b` at 0.91" is a fact a reviewer can disagree with,
12
+ * where a paragraph of reasoning is not.
13
+ *
14
+ * Degradation is "no advice", never a guess. Every path that cannot produce an answer — disabled, missing key,
15
+ * timeout, transport error, a response shape we do not recognise — returns `null`, and the caller does what it
16
+ * would have done without an advisor at all. That is why a caller must be written to work with `nullDecider`
17
+ * first, and why `nullDecider` is the default.
18
+ */
19
+ /**
20
+ * The default, and the one every caller must work correctly with.
21
+ *
22
+ * Not a placeholder: it is how advisors stay optional. A caller that behaves differently under `nullDecider` than
23
+ * under no advisor at all has made advice load-bearing, which is the one thing ADR-0077 forbids.
24
+ */
25
+ export const nullDecider = {
26
+ name: "none",
27
+ decide: async () => null,
28
+ };
29
+ //# sourceMappingURL=decider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decider.js","sourceRoot":"","sources":["../../src/advisors/decider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAqCH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAY;IAClC,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;CACzB,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Jev, through OpenRouter's Decisions endpoint — the first advisor adapter (ADR-0077).
3
+ *
4
+ * **What is verified and what is not.** The REQUEST shape below is taken from OpenRouter's own SDK reference for
5
+ * `POST /api/alpha/decisions`: `{model, state, questions}`, where each question is `noul` with `criteria.true` and
6
+ * `criteria.false`, `choice` with a `criteria` map of option to description, or `score` with a `criteria` array of
7
+ * level descriptions. That much is documented. The RESPONSE is described there only as an `answers` object beside
8
+ * `id`, `model`, `provider` and `usage`, with probabilities and confidence mentioned but never shown, and the one
9
+ * public guide to this endpoint says plainly that it has not run paid calls either. **So no shape below has been
10
+ * confirmed against a live response.** The parser therefore accepts what the documentation describes, tolerates the
11
+ * obvious variants, and returns `null` for anything else rather than guessing — which is the same thing it does when
12
+ * the endpoint is down. A live check is the `PI_DADDY_IT_JEV=1` tier, and until somebody runs it this adapter's
13
+ * response handling is a reading of documentation, not a measurement.
14
+ *
15
+ * Nothing here can widen anything: it returns `Advice`, and no kernel or governance function accepts one.
16
+ */
17
+ import type { Advice, AdviceRequest, Answer, Decider, Question } from "./decider.ts";
18
+ export declare const JEV_ENDPOINT = "https://openrouter.ai/api/alpha/decisions";
19
+ export declare const JEV_MODEL = "typesafe/jev-1.13";
20
+ /** The wire form of one question, exactly as OpenRouter's reference documents it. */
21
+ export declare function wireQuestion(question: Question): Record<string, unknown>;
22
+ export declare function wireRequest(request: AdviceRequest, model: string): Record<string, unknown>;
23
+ /**
24
+ * Read one answer out of a response, or nothing.
25
+ *
26
+ * Deliberately generous about WHERE the value and the probability sit, because the documentation names the fields
27
+ * without showing them, and strict about WHAT they are: a `choice` answer must be one of the options that were
28
+ * asked about, and a `score` must be an index into the levels. An answer outside the question's own vocabulary is
29
+ * not a low-confidence answer, it is a response we did not understand, and the honest reading of that is no advice.
30
+ */
31
+ export declare function parseAnswer(question: Question, raw: unknown): Answer | undefined;
32
+ export declare function parseAdvice(request: AdviceRequest, body: unknown): Advice | null;
33
+ export interface JevConfig {
34
+ apiKey: string;
35
+ model?: string;
36
+ endpoint?: string;
37
+ /** Injected so the adapter is testable without a network, and so nothing here reaches for a global. */
38
+ fetch?: typeof globalThis.fetch;
39
+ }
40
+ export declare function jevDecider(config: JevConfig): Decider;
41
+ //# sourceMappingURL=jev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jev.d.ts","sourceRoot":"","sources":["../../src/advisors/jev.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAErF,eAAO,MAAM,YAAY,8CAA8C,CAAC;AACxE,eAAO,MAAM,SAAS,sBAAsB,CAAC;AAE7C,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUxE;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAM1F;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CA8BhF;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAchF;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uGAAuG;IACvG,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAuBrD"}
@@ -0,0 +1,108 @@
1
+ export const JEV_ENDPOINT = "https://openrouter.ai/api/alpha/decisions";
2
+ export const JEV_MODEL = "typesafe/jev-1.13";
3
+ /** The wire form of one question, exactly as OpenRouter's reference documents it. */
4
+ export function wireQuestion(question) {
5
+ if (question.kind === "noul")
6
+ return {
7
+ type: "noul",
8
+ instructions: question.instructions,
9
+ criteria: { true: question.whenTrue, false: question.whenFalse },
10
+ };
11
+ if (question.kind === "choice")
12
+ return { type: "choice", instructions: question.instructions, criteria: { ...question.options } };
13
+ return { type: "score", instructions: question.instructions, criteria: [...question.levels] };
14
+ }
15
+ export function wireRequest(request, model) {
16
+ return {
17
+ model,
18
+ state: request.state,
19
+ questions: Object.fromEntries(Object.entries(request.questions).map(([key, q]) => [key, wireQuestion(q)])),
20
+ };
21
+ }
22
+ /**
23
+ * Read one answer out of a response, or nothing.
24
+ *
25
+ * Deliberately generous about WHERE the value and the probability sit, because the documentation names the fields
26
+ * without showing them, and strict about WHAT they are: a `choice` answer must be one of the options that were
27
+ * asked about, and a `score` must be an index into the levels. An answer outside the question's own vocabulary is
28
+ * not a low-confidence answer, it is a response we did not understand, and the honest reading of that is no advice.
29
+ */
30
+ export function parseAnswer(question, raw) {
31
+ if (raw === null || raw === undefined)
32
+ return undefined;
33
+ const object = typeof raw === "object" && !Array.isArray(raw) ? raw : undefined;
34
+ const value = object ? (object.value ?? object.answer ?? object.choice ?? object.result) : raw;
35
+ const confidenceRaw = object ? (object.confidence ?? object.probability ?? object.p) : undefined;
36
+ const confidence = typeof confidenceRaw === "number" && Number.isFinite(confidenceRaw) ? confidenceRaw : undefined;
37
+ const withConfidence = (answer) => (confidence === undefined ? answer : { ...answer, confidence });
38
+ if (question.kind === "noul") {
39
+ if (typeof value !== "boolean")
40
+ return undefined;
41
+ return withConfidence({ kind: "noul", value });
42
+ }
43
+ if (question.kind === "choice") {
44
+ if (typeof value !== "string" || !Object.hasOwn(question.options, value))
45
+ return undefined;
46
+ return withConfidence({ kind: "choice", value });
47
+ }
48
+ // ONE reading: a score is a 0-based index into the `criteria` array that was asked about, and `level` carries the
49
+ // string so a caller never indexes the number itself.
50
+ //
51
+ // The first draft accepted both a 0-based and a 1-based reading "because the documentation shows neither", which
52
+ // pushed the ambiguity onto the caller and into the ledger: with three levels, 1 and 2 were valid under both, so
53
+ // `levels[value]` could read "high" where the model meant "mid". An advisor exists to remove that guess, not to
54
+ // relocate it. **The 0-based reading is an assumption** — it indexes the documented array form — and it is
55
+ // unverified for the same reason everything else about the response is: no live call has been made. If Jev is
56
+ // 1-based, its top level falls outside the array and the whole answer is refused as unrecognised, which is the
57
+ // loud failure rather than a silently shifted one, and the `PI_DADDY_IT_JEV=1` tier is what would show it.
58
+ if (typeof value !== "number" || !Number.isInteger(value))
59
+ return undefined;
60
+ if (value < 0 || value >= question.levels.length)
61
+ return undefined;
62
+ return withConfidence({ kind: "score", value, level: question.levels[value] });
63
+ }
64
+ export function parseAdvice(request, body) {
65
+ if (typeof body !== "object" || body === null)
66
+ return null;
67
+ const envelope = body;
68
+ const raw = envelope.answers;
69
+ if (typeof raw !== "object" || raw === null)
70
+ return null;
71
+ const answers = {};
72
+ for (const [key, question] of Object.entries(request.questions)) {
73
+ const parsed = parseAnswer(question, raw[key]);
74
+ // Every question or none: a caller that asked two questions and silently received one would have to guess which
75
+ // of its branches the missing answer belonged to, and guessing is what an advisor exists to remove.
76
+ if (!parsed)
77
+ return null;
78
+ answers[key] = parsed;
79
+ }
80
+ return { answers, ...(typeof envelope.model === "string" ? { model: envelope.model } : {}) };
81
+ }
82
+ export function jevDecider(config) {
83
+ return {
84
+ name: "jev",
85
+ async decide(request, signal) {
86
+ const send = config.fetch ?? globalThis.fetch;
87
+ const response = await send(config.endpoint ?? JEV_ENDPOINT, {
88
+ method: "POST",
89
+ headers: { "content-type": "application/json", authorization: `Bearer ${config.apiKey}` },
90
+ body: JSON.stringify(wireRequest(request, config.model ?? JEV_MODEL)),
91
+ ...(signal ? { signal } : {}),
92
+ });
93
+ // A dead endpoint and an advisor with nothing to say must not read alike in the ledger: the whole reason for
94
+ // recording the nothing-cases is that an advisor which quietly stopped answering should not look like one
95
+ // nobody called. Thrown, so `createAdvisor` records `error` rather than `declined`; it catches, so nothing
96
+ // reaches the caller but `null` either way.
97
+ if (!response.ok)
98
+ throw new Error(`advisor endpoint returned ${response.status}`);
99
+ try {
100
+ return parseAdvice(request, await response.json());
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ },
106
+ };
107
+ }
108
+ //# sourceMappingURL=jev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jev.js","sourceRoot":"","sources":["../../src/advisors/jev.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Whether an advisor is on, and which one (ADR-0077).
3
+ *
4
+ * **Default off, and off is the whole configuration when nothing says otherwise.** An advisor sends a description
5
+ * of the caller's situation to a third party, so it is not something a package turns on for somebody: it is turned
6
+ * on in `.pi/pi-daddy/settings.json`, the file an operator reviews and commits, beside the grant that governs
7
+ * everything else. Malformed configuration disables the advisor and says so, which is this project's rule for
8
+ * configuration everywhere: a typo must not be a way to enable something.
9
+ *
10
+ * **Not a dashboard toggle**, which is what the programme originally sketched. The dashboard is a read-only
11
+ * renderer in a separate process that "never affects enforcement" (ADR-0036), and a control there that wrote to
12
+ * settings would be the first thing it ever wrote. Turning an advisor on is an operator decision that belongs in
13
+ * the reviewable file; `/grants` reports what is in force. That is a deliberate departure from the roadmap line.
14
+ *
15
+ * The key is never in this file. It is read from the environment, because a settings file is committed and an API
16
+ * key must not be.
17
+ */
18
+ export { ENV_ADVISOR_KEY as ADVISOR_KEY_ENV } from "../kernel/env-names.ts";
19
+ export interface AdvisorSettings {
20
+ enabled: boolean;
21
+ /** The only decider this release knows besides the null one. */
22
+ decider: "none" | "jev";
23
+ /** Overrides the adapter's pinned model id; absent means the adapter's own default. */
24
+ model?: string;
25
+ timeoutMs?: number;
26
+ /** Why an advisor is off when the settings asked for one on — reported, never silently applied. */
27
+ refusal?: string;
28
+ }
29
+ export declare const ADVISOR_OFF: AdvisorSettings;
30
+ /**
31
+ * Read the `advisor` block of a project settings file. Absent is off; malformed is off WITH a reason.
32
+ *
33
+ * The reason matters more than it looks: an operator who wrote `"enabeld": true` and got silence would conclude the
34
+ * feature does not work, and an operator who wrote it and got an advisor anyway would have a third party reading
35
+ * their session without having successfully asked for it. Both are worse than a sentence naming the field.
36
+ */
37
+ export declare function advisorSettingsFrom(raw: unknown, env?: NodeJS.ProcessEnv): AdvisorSettings;
38
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/advisors/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,eAAe,IAAI,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG5E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mGAAmG;IACnG,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,WAAW,EAAE,eAAoE,CAAC;AAE/F;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,eAAe,CA8BvG"}