@punica/editor 1.24.0 → 1.25.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/index.bundle.esm.js +2 -2
- package/dist/index.bundle.esm.js.map +1 -1
- package/dist/index.bundle.umd.js +2 -2
- package/dist/index.bundle.umd.js.map +1 -1
- package/package.json +2 -1
- package/types/punica.module.capability.d.ts +54 -0
- package/types/punica.module.kernel.ai.d.ts +14 -0
- package/types/punica.module.kernel.llm.d.ts +12 -0
- package/types/punica.module.kernel.policy.d.ts +41 -0
- package/types/punica.module.runtime.llm.d.ts +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@punica/editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"description": "Punica Editor",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"check:permissions": "node scripts/check-permissions.mjs",
|
|
27
27
|
"check:empty-states": "node scripts/check-empty-states.mjs",
|
|
28
28
|
"check:capability-handlers": "node scripts/check-capability-handlers.mjs",
|
|
29
|
+
"report:capability-actions": "node scripts/report-capability-actions.mjs",
|
|
29
30
|
"test": "vitest run",
|
|
30
31
|
"test:watch": "vitest",
|
|
31
32
|
"test:ui": "vitest --ui",
|
|
@@ -176,6 +176,45 @@ declare module 'punica' {
|
|
|
176
176
|
[key: string]: unknown;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* What a capability DOES, from a closed vocabulary — the axis a policy
|
|
181
|
+
* rule can address when the id space cannot.
|
|
182
|
+
*
|
|
183
|
+
* The substrate ships two ids for the same action more than once:
|
|
184
|
+
* `fs.readFile` and `workspace.readFile` read the same file, so a rule
|
|
185
|
+
* denying `fs.*` says nothing about the second one. Measured
|
|
186
|
+
* 2026-08-30 with a model in the loop and again without one: same
|
|
187
|
+
* workspace, same path, identical `argsBinding`, one denied and one
|
|
188
|
+
* allowed. The rule engine was right; the vocabulary the rules address
|
|
189
|
+
* was too narrow. A rule may now select `action: [file.read]` and
|
|
190
|
+
* cover both.
|
|
191
|
+
*
|
|
192
|
+
* Closed on purpose, unlike the open `sideEffects` list beside it. Two
|
|
193
|
+
* reasons: an open vocabulary cannot be relied on by a rule author who
|
|
194
|
+
* has to guess the spelling, and `extension-policy-export` expands an
|
|
195
|
+
* action rule back into ids before it leaves the machine — an
|
|
196
|
+
* expansion is only sound while both sides agree on the term.
|
|
197
|
+
*
|
|
198
|
+
* Declaring is voluntary and its absence changes nothing: a capability
|
|
199
|
+
* with no `action` is matched by id exactly as it was before this
|
|
200
|
+
* existed. There is no implicit deny anywhere in this file.
|
|
201
|
+
*/
|
|
202
|
+
export type CapabilityAction =
|
|
203
|
+
| 'file.read'
|
|
204
|
+
| 'file.write'
|
|
205
|
+
| 'file.list'
|
|
206
|
+
| 'file.stat'
|
|
207
|
+
| 'secret.read'
|
|
208
|
+
| 'secret.write'
|
|
209
|
+
| 'process.exec'
|
|
210
|
+
| 'net.request'
|
|
211
|
+
| 'model.invoke'
|
|
212
|
+
| 'memory.read'
|
|
213
|
+
| 'memory.write'
|
|
214
|
+
| 'vcs.read'
|
|
215
|
+
| 'vcs.write'
|
|
216
|
+
| 'flow.run';
|
|
217
|
+
|
|
179
218
|
/**
|
|
180
219
|
* Unified Capability Definition - Base interface for all capability types.
|
|
181
220
|
*
|
|
@@ -267,6 +306,21 @@ declare module 'punica' {
|
|
|
267
306
|
*/
|
|
268
307
|
sideEffects?: string[];
|
|
269
308
|
|
|
309
|
+
/**
|
|
310
|
+
* What this capability does, from the closed `CapabilityAction`
|
|
311
|
+
* vocabulary — the axis `.punica/policy.yaml` rules can address
|
|
312
|
+
* instead of an id.
|
|
313
|
+
*
|
|
314
|
+
* A list rather than one value because a capability can genuinely do
|
|
315
|
+
* two things: `fs.copy` reads a file and writes another, and
|
|
316
|
+
* declaring only the write would hide half of what a rule needs to
|
|
317
|
+
* see. Declare what the call PERFORMS, not what its name suggests —
|
|
318
|
+
* `search.query` returns matching lines, so it reads files.
|
|
319
|
+
*
|
|
320
|
+
* Contract: `docs/capability-actions.md`.
|
|
321
|
+
*/
|
|
322
|
+
action?: CapabilityAction[];
|
|
323
|
+
|
|
270
324
|
/**
|
|
271
325
|
* Required context for this capability to execute.
|
|
272
326
|
* Examples: ['workspace', 'active-file', 'selection']
|
|
@@ -132,6 +132,20 @@ declare module 'punica' {
|
|
|
132
132
|
usage?: runtime.LlmUsage;
|
|
133
133
|
/** Wall-clock duration of this turn's LLM call, in milliseconds. */
|
|
134
134
|
latencyMs?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Why the turn ended without a model response. Absent on a turn
|
|
137
|
+
* that produced one, which is every turn in a run that finished
|
|
138
|
+
* normally.
|
|
139
|
+
*
|
|
140
|
+
* The field exists because the turn's span has to be published
|
|
141
|
+
* either way: the model call and any compaction before it are
|
|
142
|
+
* already children of it, and a parent nobody emits leaves the
|
|
143
|
+
* reader a tree of ghosts. So a failed turn is a node that says
|
|
144
|
+
* what happened to it rather than a node that is missing.
|
|
145
|
+
*/
|
|
146
|
+
outcome?: 'error' | 'cancelled';
|
|
147
|
+
/** The failure text, when `outcome` is `'error'`. */
|
|
148
|
+
error?: string;
|
|
135
149
|
}
|
|
136
150
|
|
|
137
151
|
/**
|
|
@@ -138,6 +138,18 @@ declare module 'punica' {
|
|
|
138
138
|
modelId?: string;
|
|
139
139
|
provider?: string;
|
|
140
140
|
cacheHit?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Content digest of the prompt that produced this answer —
|
|
143
|
+
* see `runtime.LlmCallMeta.promptVersion`.
|
|
144
|
+
*
|
|
145
|
+
* It rides on the OUTPUT rather than only on the `llm.call`
|
|
146
|
+
* event because that event is published `kind: 'info'` and
|
|
147
|
+
* dies with the in-memory ring, while a capability's output
|
|
148
|
+
* reaches the durable audit record. An evidence package
|
|
149
|
+
* answering "which prompt produced this run" has to read it
|
|
150
|
+
* from disk.
|
|
151
|
+
*/
|
|
152
|
+
promptVersion?: string;
|
|
141
153
|
};
|
|
142
154
|
}
|
|
143
155
|
|
|
@@ -16,6 +16,20 @@ declare module 'punica' {
|
|
|
16
16
|
* Identifier within the kind (e.g. capabilityId, providerId).
|
|
17
17
|
*/
|
|
18
18
|
id: string;
|
|
19
|
+
/**
|
|
20
|
+
* What the guarded action performs, from the closed
|
|
21
|
+
* `CapabilityAction` vocabulary — read off the capability's own
|
|
22
|
+
* declaration by the gate, never invented here.
|
|
23
|
+
*
|
|
24
|
+
* This is what lets a rule cover a synonym: `fs.readFile` and
|
|
25
|
+
* `workspace.readFile` are two ids for one action, and before
|
|
26
|
+
* this field a rule about the first was silent about the second.
|
|
27
|
+
*
|
|
28
|
+
* Absent for a request with no capability behind it (`llm.remote`,
|
|
29
|
+
* a flow step) and for a capability that declares nothing — both
|
|
30
|
+
* key exactly as they did before actions existed.
|
|
31
|
+
*/
|
|
32
|
+
action?: CapabilityAction[];
|
|
19
33
|
risk: PolicyRisk;
|
|
20
34
|
approval: PolicyApproval;
|
|
21
35
|
/**
|
|
@@ -376,6 +390,27 @@ declare module 'punica' {
|
|
|
376
390
|
kind: string;
|
|
377
391
|
/** Exact id, or a trailing-`*` prefix glob. Absent matches every id of the kind. */
|
|
378
392
|
id?: string;
|
|
393
|
+
/**
|
|
394
|
+
* Which actions this rule is about, from the closed
|
|
395
|
+
* `CapabilityAction` vocabulary — the selector that covers a
|
|
396
|
+
* synonym the id space hides. A rule denying `fs.*` said nothing
|
|
397
|
+
* about `workspace.readFile`, which reads the same file
|
|
398
|
+
* (measured 2026-08-30); `action: ['file.read']` covers both.
|
|
399
|
+
*
|
|
400
|
+
* ANDed with `id` when both are present. Absent matches on id
|
|
401
|
+
* alone, which is every rule written before this existed.
|
|
402
|
+
*
|
|
403
|
+
* The quantifier depends on the effect, for the same reason a
|
|
404
|
+
* wildcard condition's does. For `deny`/`require`, ANY declared
|
|
405
|
+
* action named here is enough. For `allow`, EVERY action the
|
|
406
|
+
* capability declares must be named — otherwise an allow written
|
|
407
|
+
* for `file.write` would also release `fs.copy`, which reads.
|
|
408
|
+
*
|
|
409
|
+
* A capability that declares no action is matched by id only; an
|
|
410
|
+
* action rule never reaches it. Contract:
|
|
411
|
+
* `docs/capability-actions.md`.
|
|
412
|
+
*/
|
|
413
|
+
action?: CapabilityAction[];
|
|
379
414
|
effect: PolicyRuleEffect;
|
|
380
415
|
/** ANDed. Absent or empty matches every argument tuple. */
|
|
381
416
|
when?: PolicyCondition[];
|
|
@@ -395,6 +430,12 @@ declare module 'punica' {
|
|
|
395
430
|
reason?: string;
|
|
396
431
|
/** Digest of the template's rules — which policy text was in force. */
|
|
397
432
|
digest?: string;
|
|
433
|
+
/**
|
|
434
|
+
* Which selector matched: the capability's id, or an action it
|
|
435
|
+
* declares. A reader of the audit record can otherwise not tell
|
|
436
|
+
* why a rule naming no id applied to this call.
|
|
437
|
+
*/
|
|
438
|
+
matchedBy?: 'id' | 'action';
|
|
398
439
|
}
|
|
399
440
|
|
|
400
441
|
/** A rule that matched, plus the rule itself for the caller to read. */
|
|
@@ -252,6 +252,29 @@ declare module 'punica' {
|
|
|
252
252
|
* "gpt-4o", "llama3:70b-q4_K_M").
|
|
253
253
|
*/
|
|
254
254
|
model: string;
|
|
255
|
+
/**
|
|
256
|
+
* Which prompt was used, as a content digest of the resolved
|
|
257
|
+
* instruction class — its id, system prompt, output schema and
|
|
258
|
+
* response format, SHA-256 truncated to 128 bits of hex.
|
|
259
|
+
*
|
|
260
|
+
* A digest rather than a version number because there is no
|
|
261
|
+
* version to read: no `.ic.yaml` carries one and
|
|
262
|
+
* `InstructionClass` has no such field, so any counter here would
|
|
263
|
+
* be a number nobody maintains. This is the field LLMOps 2.1 left
|
|
264
|
+
* open, and the decision it was waiting for.
|
|
265
|
+
*
|
|
266
|
+
* Deliberately NOT covered: `temperature`, `maxTokens`,
|
|
267
|
+
* `expertModelId` and the provider chain. Those are how the call
|
|
268
|
+
* was made rather than what was asked, and the model that
|
|
269
|
+
* answered is already on `model` beside this. Two calls sharing a
|
|
270
|
+
* `promptVersion` were asked the same thing — not necessarily
|
|
271
|
+
* under the same conditions.
|
|
272
|
+
*
|
|
273
|
+
* Absent on a path that resolves no instruction class
|
|
274
|
+
* (`llm.chat`, `llm.chatStream`), which is every call the model
|
|
275
|
+
* makes directly rather than through a named prompt.
|
|
276
|
+
*/
|
|
277
|
+
promptVersion?: string;
|
|
255
278
|
promptTokens: number;
|
|
256
279
|
completionTokens: number;
|
|
257
280
|
/**
|