@punica/editor 1.40.0 → 1.42.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@punica/editor",
3
- "version": "1.40.0",
3
+ "version": "1.42.0",
4
4
  "description": "Punica Editor",
5
5
  "private": false,
6
6
  "type": "module",
@@ -60,6 +60,24 @@ declare module 'punica' {
60
60
  * to a name, not to an action.
61
61
  */
62
62
  argsPreview?: unknown;
63
+ /**
64
+ * The widest grant the rule in force accepts
65
+ * (`kernel.PolicyRequest.maxScope`). A card offers no wider
66
+ * choice, and `approvePending` clamps a wider one to it.
67
+ */
68
+ maxScope?: ApprovalScope;
69
+ /**
70
+ * Who may answer (`kernel.PolicyRequest.approvers`). A card whose
71
+ * host identity matches no entry offers no grant and asks a
72
+ * reviewer instead.
73
+ */
74
+ approvers?: readonly string[];
75
+ /**
76
+ * Set by `referPending`: the question was handed to a reviewer
77
+ * outside this host, under this name. A run waiting on it holds
78
+ * for the reviewer's answer instead of timing out.
79
+ */
80
+ referredTo?: string;
63
81
  correlationId?: string;
64
82
  /**
65
83
  * Trace id of the invocation that hit the gate (an agent run id
@@ -86,6 +104,29 @@ declare module 'punica' {
86
104
  * agent turn, paired with how it resolved after dispatch through
87
105
  * the gateway.
88
106
  */
107
+ /**
108
+ * A person's verdict on a cell an agent wrote, as
109
+ * `extension-ipynb-viewer` publishes it: `ai.cellKept`,
110
+ * `ai.cellRejected`, `ai.cellRestored`, each `kind: 'audit'` under the
111
+ * trace of the call that made the cell. The kernel relays a rejection
112
+ * or a restore to the AI session named on the record as one user-role
113
+ * note (`name: 'notebook'`), so the loop's next run in that
114
+ * conversation knows. Contract: `docs/cell-provenance.md`.
115
+ */
116
+ export interface CellReviewPayload {
117
+ path: string;
118
+ cellId: string;
119
+ kind: 'insert' | 'replace';
120
+ /** `origin.id` of the call that wrote the cell. */
121
+ originId: string;
122
+ runId: string;
123
+ traceId: string;
124
+ /** The conversation the call came from; absent means nobody is told. */
125
+ sessionId?: string;
126
+ /** Where the cell sat, 0-based. */
127
+ index: number;
128
+ }
129
+
89
130
  export interface AgentToolCall {
90
131
  /** Provider-supplied call id; correlates the result back to the call. */
91
132
  id: string;
@@ -109,6 +150,21 @@ declare module 'punica' {
109
150
  * auto-corrected.
110
151
  */
111
152
  repaired?: boolean;
153
+ /**
154
+ * True when a person edited the arguments on the approval card
155
+ * before the call ran (`ai.editPendingArguments`). `arguments` are
156
+ * then the person's, merged over the model's raw call by the rule
157
+ * in `approvalManagement.ts`, and the model is told so in the tool
158
+ * result it reads next.
159
+ */
160
+ edited?: boolean;
161
+ /**
162
+ * With `edited`: the fields the person changed, as they typed them
163
+ * over the redacted preview. A field they removed is absent. Never
164
+ * the merged raw call, so a masked value they left alone stays out
165
+ * of every transcript this reaches.
166
+ */
167
+ editedFields?: Record<string, unknown>;
112
168
  }
113
169
 
114
170
  /**
@@ -143,8 +199,13 @@ declare module 'punica' {
143
199
  * reader a tree of ghosts. So a failed turn is a node that says
144
200
  * what happened to it rather than a node that is missing.
145
201
  */
146
- outcome?: 'error' | 'cancelled';
147
- /** The failure text, when `outcome` is `'error'`. */
202
+ /**
203
+ * `blocked` when the turn's own model call was refused by a rule or
204
+ * held for a person nobody answered (1.41.0): a refusal is not an
205
+ * error, and the run then finishes `blocked`.
206
+ */
207
+ outcome?: 'error' | 'cancelled' | 'blocked';
208
+ /** The failure text, when `outcome` is `'error'` or `'blocked'`. */
148
209
  error?: string;
149
210
  }
150
211
 
@@ -286,6 +347,21 @@ declare module 'punica' {
286
347
  | 'blocked'
287
348
  | 'error';
288
349
 
350
+ /**
351
+ * How free one run is, chosen per run by the person who starts it.
352
+ *
353
+ * The stance acts on exactly one rule of the gate — the agent-mode
354
+ * profile that holds an AI actor's writes and high-risk calls for a
355
+ * person (`docs/approvals.md`) — and on nothing in
356
+ * `.punica/policy.yaml`, whose `deny` and `require` hold in every
357
+ * stance. `ask` offers the model no tools at all; `observe` refuses
358
+ * what the profile would have held, since a run that only looks has
359
+ * nobody to ask; `assist` is the profile as it stands, and what an
360
+ * absent stance means; `auto` skips the profile, so a write runs on
361
+ * its own unless a rule or the capability's own declaration holds it.
362
+ */
363
+ export type AgentStance = 'ask' | 'observe' | 'assist' | 'auto';
364
+
289
365
  /**
290
366
  * Input to a single agent run. The catalog the model may call is
291
367
  * narrowed by `toolFilter` (Faz 2 ships a curated subset; the full
@@ -325,6 +401,24 @@ declare module 'punica' {
325
401
  * could join them. Contract: `docs/agent-loops.md`.
326
402
  */
327
403
  trace?: { traceId: string; parentSpanId?: string };
404
+ /**
405
+ * The run's stance (see {@link AgentStance}). Omitted ⇒ `assist`,
406
+ * which is what every run was before stances existed. Recorded on
407
+ * `ai.agentStarted` and carried to the gate on the run's policy
408
+ * context, so the trail says under which stance a call was decided.
409
+ */
410
+ stance?: AgentStance;
411
+ /**
412
+ * The AI core that answers this run, in the form `llms.activeCore`
413
+ * writes: `local:<id>` or `remote:<id>`. Omitted ⇒ the active core,
414
+ * exactly as before. Carried on every model call of the run as the
415
+ * request's `providerId`, so the host dispatches to that core (a
416
+ * remote one through the directory `extension-llms` publishes as
417
+ * `llm.remote.routes`) and the egress guard judges the run by it;
418
+ * recorded on `ai.agentStarted` as `coreId`. One model per task is
419
+ * this field (Agent Mode §9, `docs/agent-loops.md`).
420
+ */
421
+ coreId?: string;
328
422
  /**
329
423
  * Optional capability catalog filter (mirrors
330
424
  * `capability.schemaExport`'s filter shape). When omitted, the
@@ -678,8 +772,37 @@ declare module 'punica' {
678
772
  runLastPlan(editedPlan?: AiPlan): Promise<AiRun>;
679
773
  getLastPlan(): AiPlan | null;
680
774
  getPendingApprovals(): PendingApproval[];
681
- approvePending(pendingId: string, scope: kernel.ApprovalScope): boolean;
682
- dismissPending(pendingId: string): boolean;
775
+ /**
776
+ * Record the grant a person made. `decider` names who, when the
777
+ * decision was made elsewhere and relayed by the host (a reviewer
778
+ * answering through the registry); absent, the host identity
779
+ * decides. Throws `APPROVER_NOT_ALLOWED` when the pending names
780
+ * approvers and the decider is none of them.
781
+ */
782
+ approvePending(
783
+ pendingId: string,
784
+ scope: kernel.ApprovalScope,
785
+ decider?: { decidedBy?: string; decidedRole?: string }
786
+ ): boolean;
787
+ dismissPending(
788
+ pendingId: string,
789
+ decider?: { decidedBy?: string; decidedRole?: string; reason?: string }
790
+ ): boolean;
791
+ /**
792
+ * Hand a pending question to a reviewer outside this host, by
793
+ * name (`role:ml-lead`, `user:<email>`, `owner`). Writes nothing
794
+ * to the store; publishes `ai.pendingApprovalReferred` so the run
795
+ * that asked waits for the answer instead of timing out.
796
+ */
797
+ referPending(pendingId: string, to: string): boolean;
798
+ /**
799
+ * Hand a pending question back with different arguments. No grant
800
+ * and no decline is written; the loop that asked dispatches again
801
+ * with the person's arguments and the gate asks anew, so an edit is
802
+ * a new question (a new `argsBinding`) and never an edited grant.
803
+ * Refused (`false`) when `edited` is not a plain object.
804
+ */
805
+ editPending(pendingId: string, edited: unknown): boolean;
683
806
  /**
684
807
  * Run the agentic tool-use loop: the model repeatedly decides on
685
808
  * capabilities to call as tools, the loop dispatches them through
@@ -30,6 +30,14 @@ declare module 'punica' {
30
30
  * key exactly as they did before actions existed.
31
31
  */
32
32
  action?: CapabilityAction[];
33
+ /**
34
+ * `'remote'` when the guarded call's payload would leave this
35
+ * machine, as the gateway's `egressGuard` resolved it before the
36
+ * gate asked. Absent for a call that stays here and for every
37
+ * request with no capability behind it. A rule with `egress:
38
+ * remote` reaches exactly the requests that carry this.
39
+ */
40
+ egress?: 'remote';
33
41
  risk: PolicyRisk;
34
42
  approval: PolicyApproval;
35
43
  /**
@@ -57,6 +65,20 @@ declare module 'punica' {
57
65
  * user consented to a hash they never saw.
58
66
  */
59
67
  argsPreview?: unknown;
68
+ /**
69
+ * The widest grant the rule in force accepts, set by the gate from
70
+ * the decision before it asks (`PolicyDecision.maxScope`) so the
71
+ * prompt offers no wider button and `approvePending` records no
72
+ * wider grant. Absent means the approver chooses.
73
+ */
74
+ maxScope?: ApprovalScope;
75
+ /**
76
+ * Who may answer, from the deciding rule's `approvers:` (set by the
77
+ * gate from `PolicyDecision.approvers`, like `maxScope`). A grant is
78
+ * consulted and recorded only under a decider one entry names;
79
+ * absent means anyone at this host may decide.
80
+ */
81
+ approvers?: readonly string[];
60
82
  }
61
83
 
62
84
  /**
@@ -150,6 +172,12 @@ declare module 'punica' {
150
172
  approval: PolicyApproval;
151
173
  reason?: string;
152
174
  decidedBy?: string;
175
+ /**
176
+ * Under which name the decider satisfied the rule's `approvers:`
177
+ * (`owner`, `role:ml-lead`, `user:<email>`). Absent on a grant no
178
+ * rule named approvers for.
179
+ */
180
+ decidedRole?: string;
153
181
  timestampMs: number;
154
182
  /**
155
183
  * Optional chain reference — Sub-step 3.L. When present,
@@ -188,6 +216,14 @@ declare module 'punica' {
188
216
  mode: PolicyMode;
189
217
  requiresApproval: boolean;
190
218
  suggestedScope?: ApprovalScope;
219
+ /**
220
+ * The widest grant the deciding `require` rule accepts (its
221
+ * `scope:`). Grants wider than this were not consulted. Absent
222
+ * when no rule wrote one.
223
+ */
224
+ maxScope?: ApprovalScope;
225
+ /** The deciding `require` rule's `approvers:`, when it wrote them. */
226
+ approvers?: readonly string[];
191
227
  reason?: string;
192
228
  /**
193
229
  * Set when a rule from a policy template decided this. Absent
@@ -263,6 +299,15 @@ declare module 'punica' {
263
299
  ctx?: {
264
300
  workspaceId?: string;
265
301
  decidedBy?: string;
302
+ /**
303
+ * The name the decider decides under, when a trusted caller
304
+ * relays a decision made elsewhere (a reviewer answering
305
+ * through the registry): `owner`, `role:<name>` or
306
+ * `user:<email>`. With `req.approvers` set and this absent,
307
+ * the host identity's roles are matched instead; a decider
308
+ * no entry names is refused with `APPROVER_NOT_ALLOWED`.
309
+ */
310
+ decidedRole?: string;
266
311
  /**
267
312
  * Time-bound the approval — Sub-step 3.F. Epoch ms;
268
313
  * `hasApproval` rejects after this point. Undefined =
@@ -307,6 +352,8 @@ declare module 'punica' {
307
352
  ctx?: {
308
353
  workspaceId?: string;
309
354
  decidedBy?: string;
355
+ /** As on `recordApproval`: the name a relayed decider decided under. */
356
+ decidedRole?: string;
310
357
  /** Why, in the decider's words. Stored verbatim. */
311
358
  reason?: string;
312
359
  traceId?: string;
@@ -460,11 +507,40 @@ declare module 'punica' {
460
507
  * `docs/capability-actions.md`.
461
508
  */
462
509
  action?: CapabilityAction[];
510
+ /**
511
+ * Selects the calls whose payload would leave this machine — a
512
+ * model call bound for a remote provider, a capability declaring
513
+ * `net.request` — as the gate resolved it. `deny egress: remote`
514
+ * is the sentence "no data leaves this machine" for every governed
515
+ * call; the direct model door is governed by `kind: llm.remote`
516
+ * (`docs/sensitive-data-egress.md`). The only value is `remote`.
517
+ */
518
+ egress?: 'remote';
463
519
  effect: PolicyRuleEffect;
464
520
  /** ANDed. Absent or empty matches every argument tuple. */
465
521
  when?: PolicyCondition[];
466
522
  /** Which approval `effect: 'require'` demands. Default `step`. */
467
523
  approval?: 'none' | 'plan' | 'step';
524
+ /**
525
+ * The widest grant that satisfies this rule, only on
526
+ * `effect: 'require'`. `once` means every call is asked about;
527
+ * `workspace` lets a project-wide grant stand and ignores an
528
+ * `always`; absent is `always`, which is what every rule written
529
+ * before this field meant — the approver chooses. An approver may
530
+ * go below the rule's scope and never above it: a wider grant on
531
+ * the store is simply not consulted, so tightening a rule takes
532
+ * effect on the next call without anyone revoking anything.
533
+ */
534
+ scope?: ApprovalScope;
535
+ /**
536
+ * Who may answer, only on `effect: 'require'`: `owner`,
537
+ * `role:<name>` or `user:<email>`, any one of which is enough. A
538
+ * grant recorded by anyone else does not satisfy the rule, and a
539
+ * host whose identity matches no entry cannot grant it — it asks
540
+ * a reviewer instead. Absent means anyone at the host decides,
541
+ * which is what every rule written before this field meant.
542
+ */
543
+ approvers?: string[];
468
544
  /** Shown to the user on a denial and written into the audit record. */
469
545
  reason?: string;
470
546
  }
@@ -477,6 +553,10 @@ declare module 'punica' {
477
553
  ruleIndex: number;
478
554
  effect: PolicyRuleEffect;
479
555
  reason?: string;
556
+ /** The rule's `scope`, when it wrote one (`effect: require` only). */
557
+ scope?: ApprovalScope;
558
+ /** The rule's `approvers`, when it wrote them (`effect: require` only). */
559
+ approvers?: string[];
480
560
  /** Digest of the template's rules — which policy text was in force. */
481
561
  digest?: string;
482
562
  /**
@@ -484,7 +564,7 @@ declare module 'punica' {
484
564
  * declares. A reader of the audit record can otherwise not tell
485
565
  * why a rule naming no id applied to this call.
486
566
  */
487
- matchedBy?: 'id' | 'action';
567
+ matchedBy?: 'id' | 'action' | 'egress';
488
568
  }
489
569
 
490
570
  /** A rule that matched, plus the rule itself for the caller to read. */
@@ -235,6 +235,13 @@ declare module 'punica' {
235
235
  * invents nothing to fill it. Passing `undefined` clears it.
236
236
  */
237
237
  setHostIdentity: (identity: HostIdentity | undefined) => void;
238
+ /**
239
+ * The identity the host injected, or `undefined`. Read by a card
240
+ * deciding whether the person at this host may answer a rule that
241
+ * names its approvers (`roles`), and by a host asking a reviewer
242
+ * (`orgs`). Never a substitute for the audit record's `actor.id`.
243
+ */
244
+ getHostIdentity: () => HostIdentity | undefined;
238
245
 
239
246
  /**
240
247
  * Package signing. A host installs a signer during bootstrap so an
@@ -395,6 +402,15 @@ declare module 'punica' {
395
402
  export interface HostIdentity {
396
403
  email: string;
397
404
  displayName?: string | null;
405
+ /**
406
+ * The person's organisation roles, as the registry lists them
407
+ * (`owner`, `ml-lead`, …), across every organisation they belong
408
+ * to. Read by a `require` rule that names its `approvers:`; absent
409
+ * or empty means such a rule matches nobody at this host.
410
+ */
411
+ roles?: readonly string[];
412
+ /** The organisations behind those roles, for a host that asks a reviewer. */
413
+ orgs?: ReadonlyArray<{ id: string; name: string; role: string }>;
398
414
  }
399
415
 
400
416
  /**
@@ -61,6 +61,13 @@ declare module 'punica' {
61
61
  mode: 'suggest' | 'execute';
62
62
  /** Approval token (if approval was granted) */
63
63
  approvalToken?: string;
64
+ /**
65
+ * The agent run's stance, set by the kernel loop from
66
+ * `AgentRunInput.stance` and read by the gate's agent-mode rule alone
67
+ * (`kernel.AI.AgentStance`). Meaningful only for an `ai` actor; absent
68
+ * means the profile as it stands.
69
+ */
70
+ stance?: 'ask' | 'observe' | 'assist' | 'auto';
64
71
  }
65
72
 
66
73
  /**
@@ -423,6 +423,18 @@ declare module 'punica' {
423
423
  * this to gate which dispatches the provider can serve.
424
424
  */
425
425
  capabilities: Set<LlmProviderCapability>;
426
+ /**
427
+ * Where a request would run, said BEFORE dispatch: `'local'` when
428
+ * the answer stays on this machine, `'remote'` when it leaves it,
429
+ * nothing when the provider cannot say. Read by the facade's remote
430
+ * gate and the gateway's `egressGuard` after the request's own
431
+ * `route` / `providerId` and before the provider's `id`; a provider
432
+ * that says nothing is judged by its id, and one with no id either
433
+ * is treated as remote (`docs/sensitive-data-egress.md`).
434
+ */
435
+ describeDestination?(
436
+ req: LlmChatRequest
437
+ ): 'local' | 'remote' | undefined | Promise<'local' | 'remote' | undefined>;
426
438
  chat(req: LlmChatRequest): Promise<LlmChatResponse>;
427
439
  /**
428
440
  * Streaming chat. Optional — providers that don't support
@@ -608,6 +620,10 @@ declare module 'punica' {
608
620
 
609
621
  export interface LlmApi {
610
622
  chat(req: LlmChatRequest): Promise<LlmChatResponse>;
623
+ /** See `LlmProvider.describeDestination`; a host registers an `LlmApi`. */
624
+ describeDestination?(
625
+ req: LlmChatRequest
626
+ ): 'local' | 'remote' | undefined | Promise<'local' | 'remote' | undefined>;
611
627
  /**
612
628
  * Optional streaming chat. Hosts that back a streaming-capable
613
629
  * provider expose it here; the substrate runtime facade wraps it