@plurnk/plurnk-contracts 1.9.1 → 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.
Files changed (44) hide show
  1. package/SPEC.md +43 -14
  2. package/dist/conformance/agui-v1.json +32 -1
  3. package/dist/plurnk.gemma.gbnf +2 -1
  4. package/dist/plurnk.qwen.gbnf +2 -1
  5. package/dist/schema/Plan.json +29 -20
  6. package/dist/schema/PlurnkStatement.json +611 -161
  7. package/dist/src/AcpPlanValue.d.ts.map +1 -1
  8. package/dist/src/AcpPlanValue.js +4 -1
  9. package/dist/src/AcpPlanValue.js.map +1 -1
  10. package/dist/src/ApplicationPort.d.ts +7 -0
  11. package/dist/src/ApplicationPort.d.ts.map +1 -1
  12. package/dist/src/AstBuilder.d.ts +2 -0
  13. package/dist/src/AstBuilder.d.ts.map +1 -1
  14. package/dist/src/AstBuilder.js +57 -13
  15. package/dist/src/AstBuilder.js.map +1 -1
  16. package/dist/src/PlanValue.d.ts +0 -1
  17. package/dist/src/PlanValue.d.ts.map +1 -1
  18. package/dist/src/PlanValue.js +8 -7
  19. package/dist/src/PlanValue.js.map +1 -1
  20. package/dist/src/PlurnkErrorStrategy.js +8 -8
  21. package/dist/src/PlurnkErrorStrategy.js.map +1 -1
  22. package/dist/src/PlurnkParser.d.ts.map +1 -1
  23. package/dist/src/PlurnkParser.js +18 -2
  24. package/dist/src/PlurnkParser.js.map +1 -1
  25. package/dist/src/generated/plurnkLexer.d.ts +12 -0
  26. package/dist/src/generated/plurnkLexer.d.ts.map +1 -1
  27. package/dist/src/generated/plurnkLexer.js +429 -367
  28. package/dist/src/generated/plurnkLexer.js.map +1 -1
  29. package/dist/src/generated/plurnkParser.d.ts +19 -9
  30. package/dist/src/generated/plurnkParser.d.ts.map +1 -1
  31. package/dist/src/generated/plurnkParser.js +564 -659
  32. package/dist/src/generated/plurnkParser.js.map +1 -1
  33. package/dist/src/generated/plurnkParserVisitor.d.ts +7 -0
  34. package/dist/src/generated/plurnkParserVisitor.d.ts.map +1 -1
  35. package/dist/src/generated/plurnkParserVisitor.js +6 -0
  36. package/dist/src/generated/plurnkParserVisitor.js.map +1 -1
  37. package/dist/src/index.d.ts +1 -1
  38. package/dist/src/index.d.ts.map +1 -1
  39. package/dist/src/index.js +1 -1
  40. package/dist/src/index.js.map +1 -1
  41. package/dist/src/types.generated.d.ts +1 -4
  42. package/dist/src/types.generated.d.ts.map +1 -1
  43. package/package.json +1 -1
  44. package/plurnk.md +77 -136
package/SPEC.md CHANGED
@@ -224,7 +224,7 @@ framed-turn ::= turn | fence-open turn fence-close
224
224
  turn ::= plan sep tail-0
225
225
  ```
226
226
 
227
- §gbnf-turn-shape The `gemma` transport root samples one complete
227
+ §gbnf-turn-shape Neither rail admits an empty thought: the `gemma` channel body and the `qwen` think body each begin with at least one character, so a constrained call reasons before it acts. The `gemma` transport root samples one complete
228
228
  `<|channel>thought\n … <channel|>` enclosure. A Qwen-style chat template has
229
229
  already supplied `<think>\n` when the `qwen` transport root begins, so that root
230
230
  samples the body and required `</think>` closer. Each generated artifact declares
@@ -330,6 +330,15 @@ more horizontal whitespace characters before each slot and any permutation of
330
330
  the slots admitted by that operation, at most once each. Accepted spacing and
331
331
  permutation are not second canonical spellings.
332
332
 
333
+ §heading-inline-body Text that follows the last slot on a heading line — after
334
+ horizontal whitespace, beginning with a character that cannot open a slot (not `[`,
335
+ `(`, or `<`) — is the first body line: `## EXEC0 [crm] (crm_query) {"soql": "…"}` and
336
+ `## FIND0 (src/**) /createCoder/i` parse as their canonical two-line forms. Nothing is
337
+ lost and the stored statement is canonical; the spelling is tolerated and announced:
338
+ one warning-severity advisory follows the statement, naming the heading and the rule
339
+ (body content goes immediately beneath the OP heading line), so the model learns
340
+ the form from the packet and never from silent acceptance.
341
+
333
342
  §operation-annotation A heading may end with one single-line Markdown HTML
334
343
  comment. AstBuilder strips the delimiters and surrounding horizontal whitespace
335
344
  into the statement's fixed `annotation: string | null` field. The annotation is
@@ -394,12 +403,12 @@ but that interpretation does not define KILL generally.
394
403
  §plan-value **PLAN carries one complete Plurnk Plan.** Its entries are the
395
404
  model's current working-memory inventory: durable findings are `memory`, finished
396
405
  actions are `completed`, open inquiries are `pending`, and active priorities are
397
- `in_progress`. Admission parses the JSON body — one JSON array document in any whitespace layout, including the {§json-result-rendering} spread the log projects — and supplies the neutral `medium`
398
- priority to each entry that omits it, and validates the canonical bare array:
399
- every entry has string `content`, `priority` in
400
- `high | medium | low`, and `status` in
406
+ `in_progress`. Admission parses the JSON body — one JSON array document in any whitespace layout, including the {§json-result-rendering} spread the log projects — strips unknown
407
+ entry keys (the model-facing Plan carries no priority: struck 2026-08-24 so the
408
+ log echoes only the canonical shape, starving stale-field habits), and validates
409
+ the canonical bare array: every entry has string `content` and `status` in
401
410
  `pending | in_progress | completed | memory`. A nonempty plain-text,
402
- malformed-JSON, or otherwise invalid body becomes one `medium`, `in_progress`
411
+ malformed-JSON, or otherwise invalid body becomes one `in_progress`
403
412
  entry whose content is the exact authored body; admission performs no partial
404
413
  repair or list inference. An empty body becomes the planless `[]`
405
414
  value. Each PLAN completely replaces the current Plan; it never expresses a
@@ -411,14 +420,22 @@ the canonical value and has no other runtime effect.
411
420
 
412
421
  §plan-acp-projection **Only an ACP-facing boundary projects the model-native
413
422
  Plan.** It constructs ACP's `{ "entries": [...] }` Plan object from the internal
414
- array, maps each `memory` entry to ACP `completed`, and prefixes its content with
415
- exact "Memory: " framing without duplicating an existing prefix. Every other
416
- entry field remains unchanged, and the internal value is not mutated.
423
+ array, synthesizes the ACP-required neutral `medium` priority on every entry
424
+ (the model-native Plan carries none), maps each `memory` entry to ACP
425
+ `completed`, and prefixes its content with exact "Memory: " framing without
426
+ duplicating an existing prefix. Every other entry field remains unchanged, and
427
+ the internal value is not mutated.
417
428
  The projected value validates against the separately owned ACP Plan schema pinned
418
429
  to ACP v1
419
430
  [`schema-v1.21.0`](https://github.com/agentclientprotocol/agent-client-protocol/tree/schema-v1.21.0)
420
431
  commit `272bf799f35a258c6a4107a0410ed361e83683d3`.
421
432
 
433
+ §exec-tag-signal EXEC's bracket slot names its runtime. A bracket whose first character is
434
+ a sign is a tag signal instead — a runtime never starts with `+` or `-` — so
435
+ `## EXEC0 [+fetch] (.)` and `## EXEC0 [crm] [+schema] (crm_describe)` admit: the tags
436
+ classify the log row exactly as on FIND or READ, the runtime is the explicit one or the
437
+ default shell, and each slot appears at most once.
438
+
422
439
  §log-tag-signal FIND, READ, EDIT, COPY, MOVE, and BARE canonically express additions
423
440
  as `+tag`. Because those operations have no tag-selection semantics, ANTLR also
424
441
  tolerates unsigned `tag` as an equivalent addition; `-tag` is invalid. Core
@@ -1140,7 +1157,9 @@ explicit `null` means roots. Singular and plural cardinalities are distinct
1140
1157
  contracts. Observation is not a client binding or permission grant.
1141
1158
 
1142
1159
  §application-loop-observation Loop observation exposes the durable scheduler
1143
- state and exact terminal `OperationResult` for one owned Worker. Exterior
1160
+ state, exact terminal `OperationResult`, and exact count of packet-bearing
1161
+ Turns for one owned Worker. Packetless producer Turns and physical provider
1162
+ retries do not contribute to `packetCount`. Exterior
1144
1163
  adapters consume this projection instead of reconstructing lifecycle from
1145
1164
  events or persistence; events remain the live notification edge.
1146
1165
 
@@ -1206,10 +1225,11 @@ turn-shape imperatives (begin with `# PLAN0`, end with a terminal
1206
1225
  `## EXEC0 <-1,300>`” instead of a raw `unrecognized character`. The redirect is
1207
1226
  EXEC-scoped because its signal mode is exclusive; SEND/KILL are untouched.
1208
1227
  - §matcher-body-redirect **Matcher body in the slot region.** When the
1209
- post-target header region begins with `$`, `~`, or `@`, the lexer redirects
1210
- the unambiguous matcher to the first body line instead of returning the
1211
- generic slot list. Slash-led regex and XPath are excluded because `/` can be
1212
- target data.
1228
+ post-target header region begins with `$`, `~`, or `@` with no whitespace
1229
+ before it, the lexer redirects the unambiguous matcher to body content below the
1230
+ OP heading instead of returning the generic slot list (after whitespace it is
1231
+ already the inline body, {§heading-inline-body}). Slash-led regex and XPath are
1232
+ excluded because `/` can be target data.
1213
1233
  - §combined-anchor-line-redirect **Combined anchor and line number in a scope.**
1214
1234
  A text-coordinate scope containing `@hash:L` or `@hash L` is one bounded hard
1215
1235
  error: `a scope position accepts one line coordinate; use the \`@hash\` anchor
@@ -1223,6 +1243,15 @@ turn-shape imperatives (begin with `# PLAN0`, end with a terminal
1223
1243
  \`[…]\` tag slot; a target goes in \`(…)\`. Try \`## EDIT0 (path)\``). It is
1224
1244
  gated on a path-shaped signal so a genuine additive-tag signal is not mis-steered
1225
1245
  toward a path it lacks.
1246
+ - §misplaced-annotation-advisory **Annotation in the body.** A READ or FIND whose
1247
+ body is solely an HTML comment (`<!-- … -->`) can never carry a matcher: it is
1248
+ the annotation the model put on the line below the heading. The builder takes
1249
+ the comment as the annotation when the heading has none, builds the operation
1250
+ with no body, and raises one warning-severity advisory naming what happened
1251
+ and the correct form (`## READ0 (…) <…> <!-- … -->`); the parser places the
1252
+ advisory right after its statement and the service delivers it as a
1253
+ `parse_advisory` notice with its position. A body with any other content is a
1254
+ matcher, as before.
1226
1255
 
1227
1256
  §error-shape The diagnostic class determines how much guidance the parser may
1228
1257
  provide:
@@ -61,6 +61,29 @@
61
61
  "name": "ordinary-run",
62
62
  "events": [
63
63
  { "type": "RUN_STARTED", "threadId": "fixture", "runId": "run" },
64
+ {
65
+ "type": "STATE_SNAPSHOT",
66
+ "snapshot": {
67
+ "plurnk": {
68
+ "status": {
69
+ "lifecycle": "idle",
70
+ "model": null,
71
+ "loopId": null,
72
+ "packetCount": 0,
73
+ "activity": null
74
+ }
75
+ },
76
+ "budget": {}
77
+ }
78
+ },
79
+ {
80
+ "type": "STATE_DELTA",
81
+ "delta": [
82
+ { "op": "replace", "path": "/plurnk/status/lifecycle", "value": "running" },
83
+ { "op": "replace", "path": "/plurnk/status/loopId", "value": 1 },
84
+ { "op": "replace", "path": "/plurnk/status/packetCount", "value": 1 }
85
+ ]
86
+ },
64
87
  {
65
88
  "type": "CUSTOM",
66
89
  "name": "plurnk.row",
@@ -73,6 +96,14 @@
73
96
  "tx": { "body": "Work." }
74
97
  }
75
98
  },
99
+ {
100
+ "type": "STATE_DELTA",
101
+ "delta": [
102
+ { "op": "replace", "path": "/plurnk/status/lifecycle", "value": "completed" },
103
+ { "op": "replace", "path": "/plurnk/status/loopId", "value": 1 },
104
+ { "op": "replace", "path": "/plurnk/status/activity", "value": null }
105
+ ]
106
+ },
76
107
  {
77
108
  "type": "CUSTOM",
78
109
  "name": "plurnk.terminated",
@@ -103,7 +134,7 @@
103
134
  ],
104
135
  "expect": {
105
136
  "completion": "success",
106
- "families": ["log/entry", "loop/terminated"],
137
+ "families": ["log/entry", "loop/packet", "loop/terminated"],
107
138
  "status": 200
108
139
  }
109
140
  },
@@ -114,7 +114,8 @@ rz-chan-b22 ::= ("<" rz-chan-b1 | [|chanl>toug\n] rz-chan-b0 | "e" rz-chan-b23 |
114
114
  rz-chan-b23 ::= ("<" rz-chan-b1 | [|chane>toug\n] rz-chan-b0 | "l" rz-chan-b24 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F<|chanel>toug\n] rz-chan-b0)?
115
115
  rz-chan-b24 ::= ("<" rz-chan-b1 | "|" rz-chan-b25 | [chanel>toug\n] rz-chan-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F<|chanel>toug\n] rz-chan-b0)?
116
116
  rz-chan-b25 ::= ("<" rz-chan-b1 | [|chaneltoug\n] rz-chan-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F<|chanel>toug\n] rz-chan-b0)?
117
- channel ::= "<|channel>thought\n" rz-chan-b0 "<channel|>"
117
+ rz-chan-first ::= [0-9A-Za-z]
118
+ channel ::= "<|channel>thought\n" rz-chan-first rz-chan-b0 "<channel|>"
118
119
  turn ::= plan tail-0
119
120
  framed-turn ::= turn | "```plurnk\n" turn "\n```"
120
121
  root-gemma ::= channel sep framed-turn
@@ -102,7 +102,8 @@ rz-think-b10 ::= ("<" rz-think-b1 | [thnk>\n/] rz-think-b0 | "i" rz-think-b11 |
102
102
  rz-think-b11 ::= ("<" rz-think-b1 | [thik>\n/] rz-think-b0 | "n" rz-think-b12 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F<think>\n/] rz-think-b0)?
103
103
  rz-think-b12 ::= ("<" rz-think-b1 | [thin>\n/] rz-think-b0 | "k" rz-think-b13 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F<think>\n/] rz-think-b0)?
104
104
  rz-think-b13 ::= ("<" rz-think-b1 | [think\n/] rz-think-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F<think>\n/] rz-think-b0)?
105
- qwen-tail ::= rz-think-b0 "</think>"
105
+ rz-think-first ::= [0-9A-Za-z]
106
+ qwen-tail ::= rz-think-first rz-think-b0 "</think>"
106
107
  turn ::= plan tail-0
107
108
  framed-turn ::= turn | "```plurnk\n" turn "\n```"
108
109
  root-qwen ::= qwen-tail sep framed-turn
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://schemas.plurnk.xyz/v0/Plan.json",
4
- "$comment": "Plurnk's model-native Plan is a bare array of ACP PlanEntry-shaped values with an additional durable-memory status. ACP projection is separately owned by AcpPlan.json.",
4
+ "$comment": "Plurnk's model-native Plan is a bare array of {content, status} working-memory entries \u2014 no priority; ACP projection (separately owned by AcpPlan.json) synthesizes the ACP-required priority at the edge.",
5
5
  "title": "Plan",
6
6
  "description": "Plurnk's complete model-native working-memory Plan entries.",
7
7
  "type": "array",
8
- "items": { "$ref": "#/$defs/PlanEntry" },
8
+ "items": {
9
+ "$ref": "#/$defs/PlanEntry"
10
+ },
9
11
  "$defs": {
10
12
  "PlanEntry": {
11
13
  "description": "One finding, task, or goal in the model's working-memory plan.",
@@ -15,37 +17,44 @@
15
17
  "description": "Human-readable description of what this task aims to accomplish.",
16
18
  "type": "string"
17
19
  },
18
- "priority": {
19
- "description": "The relative importance of this task.",
20
- "$ref": "#/$defs/PlanEntryPriority"
21
- },
22
20
  "status": {
23
21
  "description": "The current execution status of this task.",
24
22
  "$ref": "#/$defs/PlanEntryStatus"
25
23
  },
26
24
  "_meta": {
27
25
  "description": "Opaque entry metadata preserved through standards projection.",
28
- "type": ["object", "null"],
26
+ "type": [
27
+ "object",
28
+ "null"
29
+ ],
29
30
  "additionalProperties": true
30
31
  }
31
32
  },
32
- "required": ["content", "priority", "status"]
33
- },
34
- "PlanEntryPriority": {
35
- "description": "ACP v1 priority levels.",
36
- "oneOf": [
37
- { "type": "string", "const": "high" },
38
- { "type": "string", "const": "medium" },
39
- { "type": "string", "const": "low" }
40
- ]
33
+ "required": [
34
+ "content",
35
+ "status"
36
+ ],
37
+ "additionalProperties": false
41
38
  },
42
39
  "PlanEntryStatus": {
43
40
  "description": "ACP v1 plan-entry lifecycle states.",
44
41
  "oneOf": [
45
- { "type": "string", "const": "pending" },
46
- { "type": "string", "const": "in_progress" },
47
- { "type": "string", "const": "completed" },
48
- { "type": "string", "const": "memory" }
42
+ {
43
+ "type": "string",
44
+ "const": "pending"
45
+ },
46
+ {
47
+ "type": "string",
48
+ "const": "in_progress"
49
+ },
50
+ {
51
+ "type": "string",
52
+ "const": "completed"
53
+ },
54
+ {
55
+ "type": "string",
56
+ "const": "memory"
57
+ }
49
58
  ]
50
59
  }
51
60
  }