@punica/editor 1.39.1 → 1.41.1

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.39.1",
3
+ "version": "1.41.1",
4
4
  "description": "Punica Editor",
5
5
  "private": false,
6
6
  "type": "module",
@@ -60,6 +60,12 @@ 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;
63
69
  correlationId?: string;
64
70
  /**
65
71
  * Trace id of the invocation that hit the gate (an agent run id
@@ -86,6 +92,29 @@ declare module 'punica' {
86
92
  * agent turn, paired with how it resolved after dispatch through
87
93
  * the gateway.
88
94
  */
95
+ /**
96
+ * A person's verdict on a cell an agent wrote, as
97
+ * `extension-ipynb-viewer` publishes it: `ai.cellKept`,
98
+ * `ai.cellRejected`, `ai.cellRestored`, each `kind: 'audit'` under the
99
+ * trace of the call that made the cell. The kernel relays a rejection
100
+ * or a restore to the AI session named on the record as one user-role
101
+ * note (`name: 'notebook'`), so the loop's next run in that
102
+ * conversation knows. Contract: `docs/cell-provenance.md`.
103
+ */
104
+ export interface CellReviewPayload {
105
+ path: string;
106
+ cellId: string;
107
+ kind: 'insert' | 'replace';
108
+ /** `origin.id` of the call that wrote the cell. */
109
+ originId: string;
110
+ runId: string;
111
+ traceId: string;
112
+ /** The conversation the call came from; absent means nobody is told. */
113
+ sessionId?: string;
114
+ /** Where the cell sat, 0-based. */
115
+ index: number;
116
+ }
117
+
89
118
  export interface AgentToolCall {
90
119
  /** Provider-supplied call id; correlates the result back to the call. */
91
120
  id: string;
@@ -109,6 +138,21 @@ declare module 'punica' {
109
138
  * auto-corrected.
110
139
  */
111
140
  repaired?: boolean;
141
+ /**
142
+ * True when a person edited the arguments on the approval card
143
+ * before the call ran (`ai.editPendingArguments`). `arguments` are
144
+ * then the person's, merged over the model's raw call by the rule
145
+ * in `approvalManagement.ts`, and the model is told so in the tool
146
+ * result it reads next.
147
+ */
148
+ edited?: boolean;
149
+ /**
150
+ * With `edited`: the fields the person changed, as they typed them
151
+ * over the redacted preview. A field they removed is absent. Never
152
+ * the merged raw call, so a masked value they left alone stays out
153
+ * of every transcript this reaches.
154
+ */
155
+ editedFields?: Record<string, unknown>;
112
156
  }
113
157
 
114
158
  /**
@@ -143,8 +187,13 @@ declare module 'punica' {
143
187
  * reader a tree of ghosts. So a failed turn is a node that says
144
188
  * what happened to it rather than a node that is missing.
145
189
  */
146
- outcome?: 'error' | 'cancelled';
147
- /** The failure text, when `outcome` is `'error'`. */
190
+ /**
191
+ * `blocked` when the turn's own model call was refused by a rule or
192
+ * held for a person nobody answered (1.41.0): a refusal is not an
193
+ * error, and the run then finishes `blocked`.
194
+ */
195
+ outcome?: 'error' | 'cancelled' | 'blocked';
196
+ /** The failure text, when `outcome` is `'error'` or `'blocked'`. */
148
197
  error?: string;
149
198
  }
150
199
 
@@ -286,6 +335,21 @@ declare module 'punica' {
286
335
  | 'blocked'
287
336
  | 'error';
288
337
 
338
+ /**
339
+ * How free one run is, chosen per run by the person who starts it.
340
+ *
341
+ * The stance acts on exactly one rule of the gate — the agent-mode
342
+ * profile that holds an AI actor's writes and high-risk calls for a
343
+ * person (`docs/approvals.md`) — and on nothing in
344
+ * `.punica/policy.yaml`, whose `deny` and `require` hold in every
345
+ * stance. `ask` offers the model no tools at all; `observe` refuses
346
+ * what the profile would have held, since a run that only looks has
347
+ * nobody to ask; `assist` is the profile as it stands, and what an
348
+ * absent stance means; `auto` skips the profile, so a write runs on
349
+ * its own unless a rule or the capability's own declaration holds it.
350
+ */
351
+ export type AgentStance = 'ask' | 'observe' | 'assist' | 'auto';
352
+
289
353
  /**
290
354
  * Input to a single agent run. The catalog the model may call is
291
355
  * narrowed by `toolFilter` (Faz 2 ships a curated subset; the full
@@ -325,6 +389,24 @@ declare module 'punica' {
325
389
  * could join them. Contract: `docs/agent-loops.md`.
326
390
  */
327
391
  trace?: { traceId: string; parentSpanId?: string };
392
+ /**
393
+ * The run's stance (see {@link AgentStance}). Omitted ⇒ `assist`,
394
+ * which is what every run was before stances existed. Recorded on
395
+ * `ai.agentStarted` and carried to the gate on the run's policy
396
+ * context, so the trail says under which stance a call was decided.
397
+ */
398
+ stance?: AgentStance;
399
+ /**
400
+ * The AI core that answers this run, in the form `llms.activeCore`
401
+ * writes: `local:<id>` or `remote:<id>`. Omitted ⇒ the active core,
402
+ * exactly as before. Carried on every model call of the run as the
403
+ * request's `providerId`, so the host dispatches to that core (a
404
+ * remote one through the directory `extension-llms` publishes as
405
+ * `llm.remote.routes`) and the egress guard judges the run by it;
406
+ * recorded on `ai.agentStarted` as `coreId`. One model per task is
407
+ * this field (Agent Mode §9, `docs/agent-loops.md`).
408
+ */
409
+ coreId?: string;
328
410
  /**
329
411
  * Optional capability catalog filter (mirrors
330
412
  * `capability.schemaExport`'s filter shape). When omitted, the
@@ -680,6 +762,14 @@ declare module 'punica' {
680
762
  getPendingApprovals(): PendingApproval[];
681
763
  approvePending(pendingId: string, scope: kernel.ApprovalScope): boolean;
682
764
  dismissPending(pendingId: string): boolean;
765
+ /**
766
+ * Hand a pending question back with different arguments. No grant
767
+ * and no decline is written; the loop that asked dispatches again
768
+ * with the person's arguments and the gate asks anew, so an edit is
769
+ * a new question (a new `argsBinding`) and never an edited grant.
770
+ * Refused (`false`) when `edited` is not a plain object.
771
+ */
772
+ editPending(pendingId: string, edited: unknown): boolean;
683
773
  /**
684
774
  * Run the agentic tool-use loop: the model repeatedly decides on
685
775
  * 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,13 @@ 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;
60
75
  }
61
76
 
62
77
  /**
@@ -188,6 +203,12 @@ declare module 'punica' {
188
203
  mode: PolicyMode;
189
204
  requiresApproval: boolean;
190
205
  suggestedScope?: ApprovalScope;
206
+ /**
207
+ * The widest grant the deciding `require` rule accepts (its
208
+ * `scope:`). Grants wider than this were not consulted. Absent
209
+ * when no rule wrote one.
210
+ */
211
+ maxScope?: ApprovalScope;
191
212
  reason?: string;
192
213
  /**
193
214
  * Set when a rule from a policy template decided this. Absent
@@ -460,11 +481,31 @@ declare module 'punica' {
460
481
  * `docs/capability-actions.md`.
461
482
  */
462
483
  action?: CapabilityAction[];
484
+ /**
485
+ * Selects the calls whose payload would leave this machine — a
486
+ * model call bound for a remote provider, a capability declaring
487
+ * `net.request` — as the gate resolved it. `deny egress: remote`
488
+ * is the sentence "no data leaves this machine" for every governed
489
+ * call; the direct model door is governed by `kind: llm.remote`
490
+ * (`docs/sensitive-data-egress.md`). The only value is `remote`.
491
+ */
492
+ egress?: 'remote';
463
493
  effect: PolicyRuleEffect;
464
494
  /** ANDed. Absent or empty matches every argument tuple. */
465
495
  when?: PolicyCondition[];
466
496
  /** Which approval `effect: 'require'` demands. Default `step`. */
467
497
  approval?: 'none' | 'plan' | 'step';
498
+ /**
499
+ * The widest grant that satisfies this rule, only on
500
+ * `effect: 'require'`. `once` means every call is asked about;
501
+ * `workspace` lets a project-wide grant stand and ignores an
502
+ * `always`; absent is `always`, which is what every rule written
503
+ * before this field meant — the approver chooses. An approver may
504
+ * go below the rule's scope and never above it: a wider grant on
505
+ * the store is simply not consulted, so tightening a rule takes
506
+ * effect on the next call without anyone revoking anything.
507
+ */
508
+ scope?: ApprovalScope;
468
509
  /** Shown to the user on a denial and written into the audit record. */
469
510
  reason?: string;
470
511
  }
@@ -477,6 +518,8 @@ declare module 'punica' {
477
518
  ruleIndex: number;
478
519
  effect: PolicyRuleEffect;
479
520
  reason?: string;
521
+ /** The rule's `scope`, when it wrote one (`effect: require` only). */
522
+ scope?: ApprovalScope;
480
523
  /** Digest of the template's rules — which policy text was in force. */
481
524
  digest?: string;
482
525
  /**
@@ -484,7 +527,7 @@ declare module 'punica' {
484
527
  * declares. A reader of the audit record can otherwise not tell
485
528
  * why a rule naming no id applied to this call.
486
529
  */
487
- matchedBy?: 'id' | 'action';
530
+ matchedBy?: 'id' | 'action' | 'egress';
488
531
  }
489
532
 
490
533
  /** A rule that matched, plus the rule itself for the caller to read. */
@@ -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
@@ -163,10 +163,20 @@ declare module 'punica' {
163
163
  * during bootstrap. Kernel and runtime modules are always registered
164
164
  * unconditionally.
165
165
  *
166
- * Structural DOM slots are derived from this list:
167
- * - `shell.activityBar` → creates `activityBar` slot
168
- * - `shell.view` → creates `primarySidebar` + `contentSidebar` slots
169
- * - `shell.statusbar` → creates `statusbar` slot
166
+ * Structural DOM slots are derived from this list
167
+ * (`presentedSlots()` in `@punica/editor` is the one definition):
168
+ * - `shell.activityBar` → `activityBar`, `primarySidebar` and
169
+ * `contentSidebar` — the strip is the only gesture that opens a
170
+ * primary sidebar, and the content drawer is anchored beside it
171
+ * (until 2026-09-26 the two sidebars followed `shell.view`, which
172
+ * every profile activates for its helpers, so a window with no
173
+ * strip got a sidebar nothing could open)
174
+ * - `shell.statusbar` → `statusbar`
175
+ *
176
+ * A view whose declared home slot the profile does not present is
177
+ * placed as a tab in the `content` slot, on demand — the extension
178
+ * analysis decorator applies that rule, so an extension never reads
179
+ * the profile.
170
180
  */
171
181
  activeModules: ShellModuleId[];
172
182
  /**
@@ -12,6 +12,13 @@ declare module 'punica' {
12
12
  * agents and MCP clients all converge on them).
13
13
  */
14
14
  export namespace Views {
15
+ /**
16
+ * Where a view can be placed. `content` is never a manifest home: it is
17
+ * where a view lands when the active profile presents none of the slot
18
+ * its manifest names (`homeSlot` keeps the declared one), and a view
19
+ * placed there is a content tab, opened by a reveal and closed like a
20
+ * document.
21
+ */
15
22
  export type ViewSlotId =
16
23
  | 'primarySidebar'
17
24
  | 'secondarySidebar'