@plurnk/plurnk-contracts 3.3.0 → 4.1.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/.env.defaults CHANGED
@@ -9,3 +9,20 @@ PLURNK_PORT=1066
9
9
  # The whole URL a client dials instead: a remote portal, or a daemon bound to an address no
10
10
  # client can dial (0.0.0.0). Unset = http://$PLURNK_HOST:$PLURNK_PORT/agui.
11
11
  # PLURNK_AGUI_URL=
12
+
13
+ # Whether the daemon authenticates ({§credential-authority}): 1 = every request to the client
14
+ # interface and to the hosted agent presents a credential this daemon issued, and the daemon serves
15
+ # the authorization server that issues them; 0 = no credential exists and every request is
16
+ # anonymous, for a harness driving a daemon of its own. The posture is this value, never inferred
17
+ # from what else is set.
18
+ PLURNK_AUTH=1
19
+ # The operator's client secret ({§agui-oauth-clients}): what the terminal client and every other
20
+ # tool of yours exchanges for access tokens. Unset, the daemon mints one at every boot into the
21
+ # file `secret` beside its database (0600), where a tool running as you reads it. Set it here, and
22
+ # the same value in the tool's environment, for a tool on another host or under another user.
23
+ # PLURNK_SECRET=
24
+ # The operator's browser sign-in ({§agui-oauth-login}): the password a browser signs in with,
25
+ # compared in constant time at the authorization endpoint. No browser is the operator without it:
26
+ # while it is empty, the sign-in page says so and where to set it, and admits nobody
27
+ # ({§agui-oauth-session}). The terminal client and your other tools use PLURNK_SECRET instead.
28
+ PLURNK_PASS=
package/README.md CHANGED
@@ -52,7 +52,9 @@ Validator.assertOperationResult(result);
52
52
 
53
53
  Generated wire types, constructors, and validators share the package root entry
54
54
  point described by SPEC {§wire-entrypoint}. Owning JSON Schemas use the published
55
- `@plurnk/plurnk-contracts/schema/*.json` subpaths.
55
+ `@plurnk/plurnk-contracts/schema/*.json` subpaths. The root is runtime-neutral, so
56
+ a browser type-checks it without Node; the application port that modules consume,
57
+ which names Node's HTTP types, is `@plurnk/plurnk-contracts/port` ({§runtime-neutral-root}).
56
58
 
57
59
  ## Development
58
60
 
package/SPEC.md CHANGED
@@ -16,7 +16,7 @@ runtime-neutral wire envelopes; `@plurnk/plurnk-parser` implements the language
16
16
  | Stopped-world client contract | `ProposalDisposition`, `ProposalProjection` |
17
17
  | Client-owned interaction contract | `ClientInteractionRequest`, `ClientInteractionProjection`, `ClientInteractionResolution` |
18
18
  | Client capability presentation | `ClientDisplayCapabilities` |
19
- | Exterior adapter application calls | `ApplicationPort` |
19
+ | Exterior adapter application calls | `ApplicationPort`, from `@plurnk/plurnk-contracts/port` ({§runtime-neutral-root}) |
20
20
  | Workspace MCP configuration | `McpServerDefinition`, `McpOAuth` |
21
21
  | Worker Agent Skills definition | `SkillDefinition` |
22
22
  | Worker outbound A2A agent definition | `A2aAgentDefinition` |
@@ -295,11 +295,11 @@ wider bare fence nests where closing there would cost the reading more
295
295
  ({§fence-pairing}). A nested block ends at its first valid closer, as a CommonMark code block
296
296
  does. So a wider outer fence holds any narrower body, finished or not, and equal widths nest
297
297
  when every inner block closes. Preserve every nested body byte, including apparent operations
298
- and known executors, without dispatching them. These rules apply equally to model programs,
298
+ and executor invocations, without dispatching them. These rules apply equally to model programs,
299
299
  stored programs, client operations, and reasoning quotations.
300
300
 
301
301
  §operation-fences **Accept three or more backticks; teach and render three.** A line-start fence
302
- of three or more backticks naming a native operation or registered executor opens that
302
+ of three or more backticks carrying an operation heading ({§executor-heading}) opens that
303
303
  operation ({§indented-fences}), and no width draws a receipt. Three is the taught form and the
304
304
  rendered form; a wider fence is the same statement, and it is how a body holding fence lines of
305
305
  its own is held: an opener wider than any of them ({§balanced-fences}). Enclosing quotations
@@ -328,7 +328,7 @@ aside, an operand, or other heading text does not qualify and remains the unfenc
328
328
  read this way.
329
329
 
330
330
  §fence-heading-in-body Outside a complete nested block ({§balanced-fences}), a fence
331
- line of three or more backticks and a name that is a native operation or a known executor
331
+ line of three or more backticks carrying a heading under {§executor-heading}
332
332
  is a heading. Inside an open block it either ends that block without closing it and opens the
333
333
  next statement, or it is a literal example held by the block. {§fence-pairing} chooses between
334
334
  the two. Fence lines of
@@ -356,8 +356,8 @@ input at once by {§fence-pairing}:
356
356
  | Indented four spaces or more, or by a tab | Body ({§indented-fences}) |
357
357
  | Fewer backticks than the block's own, or another character | Body, or the opener of a nested block |
358
358
  | At least the block's backticks, bare | The block's closer, or the opener of a nested block |
359
- | Labeled, naming nothing known | The opener of a nested block, or body |
360
- | Three or more backticks naming a native operation or known executor | A heading that ends the block, or a literal example |
359
+ | Labeled, without an operation heading | The opener of a nested block, or body |
360
+ | Three or more backticks with an operation heading ({§executor-heading}) | A heading that ends the block, or a literal example |
361
361
 
362
362
  ### §fence-pairing Fence pairing
363
363
 
@@ -410,7 +410,7 @@ repaired reading: a complete nested interpretation takes precedence. Each class
410
410
  | Counted | Complete readings | Repaired readings | Why |
411
411
  |---|---|---|---|
412
412
  | Hidden operations: a heading that will not run, written at its block's width or wider | 1, together with the next row | 1 | Every operation the author wrote should run |
413
- | Repairs: a supplied closer (except a message's run to the end of the input, {§message-run-on}), a fence read as a stray, a narrower closer accepted, a body under an operation that takes none (FIND, READ, COPY, MOVE, non-log targeted KILL), a body not well-formed in the media type its runtime declares ({§executor-invocation}) | none by definition | 2 | The least-errors distance |
413
+ | Repairs: a supplied closer (except a message's run to the end of the input, {§message-run-on}), a fence read as a stray, a narrower closer accepted, a body under an operation that takes none (COPY, MOVE, non-log targeted KILL), a body not well-formed in the media type its runtime declares ({§executor-invocation}) | none by definition | 2 | The least-errors distance |
414
414
  | Literal by declaration: a heading narrower than its block, or no wider than the labeled block or example holding it, or inside a quotation or a KILL, or an executor heading inside a SEND, WORK, FORK or BARE body ({§prose-code-blocks}); a labeled fence in a body read as text | 1, together with the row above | 3 | Every code block the author declared should stand |
415
415
  | Supplied closers | 2 | 4 | A block the author opened should end at a fence the author wrote |
416
416
  | Other fences read as text | 3 | 5 | The least departure from the fences as written |
@@ -499,12 +499,11 @@ by the next opener is {§inline-chain}, and by nothing is the ordinary {§fence-
499
499
  no ambiguity to resolve: a slot begins with `<` or `[` and an opener with a backtick run, so the
500
500
  shapes are disjoint.
501
501
 
502
- §executor-case **An executor tag in any case.** A fence tag that matches a
503
- registered executor's name case-insensitively opens that executor (`SH` opens
504
- `sh`), and the statement's `executor` is the registered spelling, so a lookup
505
- by that name never misses. Operation names stay uppercase by teaching and were
506
- never written otherwise in 12,000 fences; one `SH` in 1,300 `sh` fences was. An unregistered name without an accepted spelling
507
- ({§executor-js-spelling}) is still prose ({§interstitial-fence}).
502
+ §executor-case **An executor tag in any case.** An executor heading matches a
503
+ known executor's name case-insensitively (`SH` opens `sh`); its AST carries the
504
+ registered spelling when known, otherwise the lowercase name. Native operation
505
+ names remain case-sensitive. Recognition follows {§executor-heading}; availability
506
+ and permission are checked at dispatch, not granted by a recognized spelling.
508
507
 
509
508
  §executor-js-spelling When `node` is registered and `js` is not, `js` names
510
509
  `node` under {§executor-case}. Parsing normalizes the runtime before admission
@@ -529,8 +528,8 @@ line takes the rest of the line as the aside, with one warning-severity advisory
529
528
  A closed aside followed by more text is unchanged.
530
529
 
531
530
  §quotation **A fence that opens no operation quotes.** Outside a body, a line-start fence that
532
- is not an operation heading — unlabeled, tagged like a code block (`ts`, `json`), or naming
533
- nothing registered — opens a
531
+ is not an operation heading ({§executor-heading}) — unlabeled or tagged like an unknown
532
+ code language (`ts`, `json`) without invocation slots — opens a
534
533
  quotation that runs to its matching closer (same character, width at least the opener's) or to
535
534
  the end of the input. Everything inside is data: no operation runs there and native tool-call
536
535
  markup is not read ({§native-tool-calls}). An operation fenced inside an unlabeled code block
@@ -552,23 +551,29 @@ belongs in a properly nested body ({§balanced-fences}) or a quotation ({§quota
552
551
  So a model may show plurnk's own operations in an answer. Two exceptions keep programs whole:
553
552
  CommonMark's own rule that a backtick opener's line carries no further backtick, so
554
553
  ```` ```READ (x)``` ```` is inline code and quotes nothing after it; and a bare fence directly under
555
- a line carrying a fence run, which is an orphaned closer and quotes nothing. An unknown tag at
556
- any width is a code block and draws nothing, unless its line carries a target slot, `(…)`: that
557
- is an operation the model missed by its tag, and it draws one warning — `` `OP` is not an
558
- operation or a known executor here. `` An offset example draws nothing either
554
+ a line carrying a fence run, which is an orphaned closer and quotes nothing. An unknown tag
555
+ without invocation slots is a code block and draws nothing. An offset example draws nothing either
559
556
  ({§indented-fences}). Quotation outside an operation is
560
557
  response text under {§response-text}, not an executable program or a completion envelope.
561
558
 
562
559
  `plurnk.md` names the tab offset as the form for an example, in the aside of its nesting
563
560
  example, and shows its own examples offset; the other quoting fences are untaught: a tilde
564
- fence, an unlabeled fence and an unknown tag all quote. Each is a shape a model reaches for from ordinary Markdown rather than from this
561
+ fence, an unlabeled fence and an unknown tag without invocation slots all quote. Each is a shape a model reaches for from ordinary Markdown rather than from this
565
562
  teaching, so honouring it protects an example the model already believed was safe
566
563
  ({§outside-text} places the scale). The offset is the one form that survives every
567
564
  fence style.
568
565
 
569
- §interstitial-fence Only a native operation or a known executor opens a block. A fence naming
570
- anything else, or nothing at all, opens no operation: outside a block it quotes under
571
- {§quotation}, inside a body it is body.
566
+ §executor-heading **Syntax identifies an invocation; dispatch resolves its runtime.** A native
567
+ operation or a known executor name opens a heading. An otherwise unknown fence name also opens an
568
+ executor heading when the next non-horizontal-whitespace character starts an invocation slot:
569
+ `(` for the target, `[` for metadata, or `<` for scope (not `<!--`, an aside). The same slot grammar
570
+ then validates and binds it, including malformed-slot diagnostics and permitted slot ordering.
571
+ Availability does not affect that parse. Known executors are the host's supplied vocabulary plus
572
+ `sh`; knowing a name also permits its targetless, slotless form. Enclosing quotations, indented
573
+ examples and reasoning-channel restrictions remain unchanged.
574
+
575
+ §interstitial-fence Only a heading under {§executor-heading} opens an operation. Any other fence
576
+ opens no operation: outside a block it quotes under {§quotation}, inside a body it is body.
572
577
 
573
578
  §closer-aside A closing fence followed on its line by one aside and nothing else
574
579
  is the closer; the aside is outside text. Read as body, that line would be written
@@ -655,7 +660,7 @@ beside owner metadata, not only a matcher carried inside its `pattern` option.
655
660
 
656
661
  | Element | Contract |
657
662
  |---|---|
658
- | Fence name | Reserved native OP, otherwise a registered executor or attached MCP service |
663
+ | Fence name | Reserved native OP, otherwise an executor name under {§executor-heading} |
659
664
  | `(path)` | Target/program/tool slot; COPY and MOVE each have two resource operands |
660
665
  | `[metadata]` | One owner-interpreted block; options, never the op's input |
661
666
  | `<scope>` | Operation-specific numeric or anchored coordinates |
@@ -722,20 +727,18 @@ compact empty block or permit unmatched fences.
722
727
  | KILL on a log target | The matcher. Beside an option block's `pattern`, the first line of the distillation ({§log-kill-distillation}). |
723
728
  | EDIT | A sigil-led matcher ({§edit-pattern}), unless an option block carries the `pattern`; otherwise the body. |
724
729
  | COPY, MOVE | Ignored ({§transfer-resource-selections}). |
725
- | SEND, WORK, FORK, BARE, executors | One JSON object is the option block where the heading admits one ({§bare-option-object}); otherwise the body. |
730
+ | SEND, WORK, FORK, BARE | One JSON object is the option block where the heading admits one ({§bare-option-object}); otherwise the body. |
731
+ | executors | The input body, including an unbracketed JSON object; metadata requires brackets. |
726
732
  | NOTE, WAIT, KILL without a target | The body. |
727
733
 
728
734
  §bare-option-object A bare option object is the option block, where the house option array owns the
729
- block. On an operation that takes an option block and no bare matcher — SEND, BARE, WORK, FORK and
730
- every executor fence — heading text after the slots that is exactly one JSON object `{…}`, where the
735
+ block. On SEND, BARE, WORK and FORK, heading text after the slots that is exactly one JSON object `{…}`, where the
731
736
  heading admits a block (BARE, WORK and FORK only after their target), is read
732
737
  as `[{…}]`: the AST carries the array form, the written heading renders it, and one
733
738
  warning-severity receipt names the indulgence in place of the {§heading-inline-body} advisory:
734
- "`SEND` took a bare option object; the taught form is `[{…}]`." The option array is a house
735
- convention, not a language rule ({§scheme-metadata-modifier}): an executor whose declared body is
736
- JSON ({§executor-invocation}), an MCP tool, reads the object as its body instead — its arguments,
737
- written on the heading line — under {§heading-inline-body}. The host names those executors to the parse
738
- (`ParseOptions.jsonBodyExecutors`). A heading that already carries a block keeps the object as
739
+ "`SEND` took a bare option object; the taught form is `[{…}]`." Executor headings always read an
740
+ unbracketed object as input under {§heading-inline-body}, regardless of registration or input media
741
+ type; their metadata is only the bracketed slot ({§scheme-metadata-modifier}). A heading that already carries a block keeps the object as
739
742
  inline body. On FIND, READ and KILL the same text is the matcher
740
743
  ({§naked-pattern}), because a search for JSON text is legitimate; a bare matcher
741
744
  that parses as a JSON object draws one advisory saying so: "`{…}` was read as the
@@ -744,8 +747,7 @@ matcher." ({§diagnostic-observation}).
744
747
  §operation-aside The final header modifier may be one single-line HTML
745
748
  comment. AstBuilder removes its delimiters and surrounding whitespace into
746
749
  `aside: string | null`. It is durable descriptive text, not authority,
747
- routing, timing, or body input. Comments inside a body remain literal except
748
- for the narrowly owned {§misplaced-aside-advisory}.
750
+ routing, timing, or body input. Comments inside a body remain literal.
749
751
 
750
752
  §scheme-metadata-modifier A target may carry one single-line `[metadata]`
751
753
  block, placed under {§slot-order}; executor and SEND fences also admit it without a target.
@@ -783,13 +785,10 @@ the owner verbatim, and the owner's reader treats `pattern` as reserved. The
783
785
  language lifts only from one block that parses as a JSON array of objects;
784
786
  anything else lifts nothing and reaches the owner's `400` untouched. A
785
787
  `pattern` that is present but not a string is the language's own positioned
786
- diagnostic, as is a matcher of a claimed dialect that fails admission. FIND,
787
- READ, and targeted KILL take no body: a body beneath their heading is not used and
788
- the operation still runs, with one warning-severity advisory naming the lines not
789
- used (`FIND takes no body; lines 4–6 were not used.`); it is never silently read as a
790
- matcher, and it never strikes ({§matcher-body-redirect}). The heading line itself
791
- is the matcher's home ({§naked-pattern}). A body that is only an HTML comment is
792
- still the aside under {§misplaced-aside-advisory}. EDIT keeps its literal body:
788
+ diagnostic, as is a matcher of a claimed dialect that fails admission. FIND and
789
+ READ bodies become memory under {§retrieval-annotation}; a non-log KILL's body is
790
+ unused and receives the advisory under {§matcher-body-redirect}. The heading line
791
+ is the matcher's home ({§naked-pattern}). EDIT keeps its literal body:
793
792
  with a matcher it is the replacement for every selected span ({§edit-pattern}),
794
793
  and an absent body deletes them. `PlurnkParser.stringify` writes a lifted matcher
795
794
  whose block left no metadata back bare when the bare form reads back identically
@@ -814,8 +813,8 @@ governed by {§canonical-statement}; runtime conditions remain explicit below.
814
813
 
815
814
  | OP | `(path)` | `<scope>` | `body` |
816
815
  |------|----------------------------------------------|---------------------------------|--------------------------------|
817
- | FIND | required target or glob | optional result range | empty |
818
- | READ | required target | optional text region | empty |
816
+ | FIND | required target or glob | optional result range | annotation normalized to NOTE |
817
+ | READ | required target | optional text region | annotation normalized to NOTE |
819
818
  | EDIT | required file or entry | required for an existing target | literal text |
820
819
  | COPY | required source and destination | optional region after each path | empty |
821
820
  | MOVE | required source and destination | optional region after each path | empty |
@@ -828,11 +827,22 @@ governed by {§canonical-statement}; runtime conditions remain explicit below.
828
827
  | NOTE | none | none | literal working memory |
829
828
  | WAIT | optional label ({§send-wait-scope}) | none; duration is `[seconds]` metadata | explanation of the wait |
830
829
 
830
+ §retrieval-annotation **A retrieval's authored body is memory, not selection or output.**
831
+ A nonblank body beneath FIND or READ becomes one ordinary NOTE immediately before
832
+ the retrieval, including a body consisting of a pattern or an HTML comment. A
833
+ grouped READ contributes one NOTE before its expanded statements. The retrieval
834
+ itself has no body; its selector comes only from its header. Normalization is the
835
+ same in content and exposed reasoning and never executes quoted body text. The
836
+ canonical rendering is NOTE followed by bodyless retrieval; exact submitted text
837
+ remains source evidence. The NOTE persists independently of retrieval success and
838
+ is curated normally. Explicit NOTE remains the taught form.
839
+
831
840
  §note-value NOTE retains its literal body as ordinary model-owned working memory.
832
841
  It has no target, scope, metadata, or lifecycle effect. Its full body participates
833
842
  in ordinary log token accounting and model-driven curation; prior notes are not
834
- automatically hidden. A NOTE-only turn does not request completion; ordinary
835
- repetition and strike rules still apply.
843
+ automatically hidden. Each turn may contain zero or more NOTEs; their omission
844
+ produces neither a notice nor a failure. A NOTE-only turn does not request
845
+ completion; ordinary repetition and strike rules still apply.
836
846
 
837
847
  §reasoning-operations The shared fence parser admits complete, line-leading NOTE,
838
848
  FIND and READ statements from exposed provider reasoning. Admission is part of
@@ -856,9 +866,10 @@ reasoning operations contribute neither execution nor normalization notices.
856
866
 
857
867
  §exec-executor-slot The fence name selects the executor directly: for example,
858
868
  `python3 (tools/report.py)` or `gitea (issue_list)` on the opening fence line.
859
- Reserved native OP names take precedence. Any other name is an executor fence: its
860
- AST carries the `runtime` tag and no operation keyword, then `target`, metadata, timing and body fields.
861
- Registration is checked by the runtime, not by the syntax parser. An attached
869
+ Reserved native OP names take precedence. An executor heading's AST ({§executor-heading})
870
+ carries the `runtime` tag and no operation keyword, then `target`, metadata, timing and body fields.
871
+ Registration is checked at the invocation's execution point, after earlier operations settle
872
+ ({§op-execution-order}), not by the syntax parser. An attached
862
873
  MCP service uses that executor path and its owner validates the named tool and
863
874
  input-body JSON against its schema. Unknown names do not fall back to a shell.
864
875
  There is no runtime-less form: every execution names its runtime, and canonical
@@ -1094,8 +1105,7 @@ ingestion restriction: the parser decomposes arbitrary URL authorities.
1094
1105
  ## §matcher-prefix-claims 6. Bulk pattern matching
1095
1106
 
1096
1107
  FIND, READ, KILL, EDIT, and the COPY/MOVE operands accept an optional matcher
1097
- through the `pattern` option ({§matcher-option}); the client-tier LOOK still
1098
- carries its matcher as a body. AstBuilder assigns the dialect from the
1108
+ through the `pattern` option ({§matcher-option}). AstBuilder assigns the dialect from the
1099
1109
  matcher's leading characters. A leading prefix claims its dialect. Invalid
1100
1110
  claimed syntax never falls back to glob matching: it is the operation's own
1101
1111
  refusal ({§matcher-refusal}), not a parse error.
@@ -1160,8 +1170,8 @@ statements remain recoverable when their boundaries are trustworthy.
1160
1170
 
1161
1171
  - §pattern-body-single-line Every matcher is one physical line. On the protocol
1162
1172
  operations it is the heading line's text ({§naked-pattern}) or the `pattern` option's
1163
- JSON string ({§matcher-option}), one line by construction; the client-tier LOOK still carries its matcher as a body, and AstBuilder
1164
- rejects a multiline one before dialect classification. A regex that matches a
1173
+ JSON string ({§matcher-option}); AstBuilder rejects a multiline one before dialect
1174
+ classification. A regex that matches a
1165
1175
  newline uses the two-character `\n` escape. Non-matcher operation bodies remain
1166
1176
  multiline.
1167
1177
 
@@ -1178,7 +1188,7 @@ The operation column names the canonical AST operation after
1178
1188
  | Operation | Canonical components | Meaning |
1179
1189
  |-----------------------|----------------------------------------|----------------------------------------------------------------------------|
1180
1190
  | FIND | 0–2 positions | Inclusive resource or exact-target location positions ({§find-result-unit}; markerless first page under {§markerless-first-page}) |
1181
- | READ / client LOOK | 0/1/2/4 text coordinates | Text projection from one exact selected file, entry, or log item |
1191
+ | READ | 0/1/2/4 text coordinates | Text projection from one exact selected file, entry, or log item |
1182
1192
  | EDIT | 0/1/2/4 text coordinates | Text replacement, deletion, prepend, or append |
1183
1193
  | COPY/MOVE source | 0/1/2/4 text coordinates | Region copied or moved from the selected source |
1184
1194
  | COPY/MOVE destination | 0/1/2/4 text coordinates after target | Region replaced or insertion point at the destination |
@@ -1199,7 +1209,7 @@ body, while unsupported arity is a runtime failure.
1199
1209
  spelled `@` followed by exactly five Base62 characters (`0-9A-Za-z`) wherever
1200
1210
  its `L`, `SL`, or `EL` position denotes a line. Columns, prepend `0`, and append
1201
1211
  `-1` remain numeric. Exact READ, EDIT, COPY/MOVE source and destination,
1202
- KILL, and client LOOK preserve these positions in `TextLineMarker`; core resolves them
1212
+ and KILL preserve these positions in `TextLineMarker`; core resolves them
1203
1213
  against the addressed current text before operation-specific numeric scope
1204
1214
  semantics run. FIND's result positions remain numeric and reject anchors. Numeric text scopes remain
1205
1215
  canonical and fully supported. Parser acceptance does not imply model-facing
@@ -1386,8 +1396,8 @@ reports how many recorded turns concluded in a way the current contract would re
1386
1396
 
1387
1397
  ## 12. Public API
1388
1398
 
1389
- The package root is the single JavaScript and TypeScript entry point. Shared AST
1390
- and wire types come from generated schemas; the small hand-maintained parser
1399
+ The package root is the single JavaScript and TypeScript entry point for the language and wire
1400
+ contracts ({§runtime-neutral-root}). Shared AST and wire types come from generated schemas; the small hand-maintained parser
1391
1401
  types cover ordered parse items and `PlurnkParseError`, which JSON Schema cannot
1392
1402
  express. Consumers never receive ANTLR parse-tree or token types.
1393
1403
 
@@ -1415,7 +1425,6 @@ blocks themselves are the program.
1415
1425
  |--------------------------------|----------------------------------------------------------------|-----------------------|
1416
1426
  | `PlurnkParser.parse` | One model turn, including recoverable outside text and operation diagnostics | `PlurnkStatement` |
1417
1427
  | `PlurnkParser.parseStatements` | Zero or more protocol statements | `PlurnkStatement` |
1418
- | `PlurnkParser.parseClient` | Executable blocks, including the read-shaped LOOK command | `ClientStatement` |
1419
1428
 
1420
1429
  Every entry point returns ordered `statement` and `error` items. The model-turn
1421
1430
  entry point also returns `text` items under {§response-text}. When present,
@@ -1423,6 +1432,14 @@ entry point also returns `text` items under {§response-text}. When present,
1423
1432
  extent. The statement `op` field discriminates the generated per-operation
1424
1433
  union.
1425
1434
 
1435
+ §runtime-neutral-root **The package root is runtime-neutral.** A browser type-checks it with DOM
1436
+ libraries and no Node globals: nothing it reaches names a Node module or global. The application port
1437
+ ({§application-port}), whose HTTP host hands an adapter Node's request and response ({§http-host}), is
1438
+ the one Node-bound contract and its own types-only entry, `@plurnk/plurnk-contracts/port`, with
1439
+ `HttpHost` and `HttpRouteHandler`; the types it speaks stay in the root. A helper that reads a request
1440
+ takes only its headers (`RequestHeaders`), which a Node request satisfies. Lint checks the root's whole
1441
+ source graph under those settings (`tsconfig.browser.json`).
1442
+
1426
1443
  §root-value-api The package-root runtime namespace is closed. Its exact membership has one home,
1427
1444
  `src/index.test.ts`, which fails on any addition or removal; the table names the families and
1428
1445
  their owners. All other root exports are TypeScript types. `PlurnkParser` and `parsePath` are not
@@ -1443,8 +1460,8 @@ among them: the parser is `@plurnk/plurnk-parser`'s ({§parser-consumers}).
1443
1460
 
1444
1461
  The remaining values are small pure helpers over those contracts (`isExecution`, `writtenOp`,
1445
1462
  `lifecycleOfLoopStatus`, `selectWorkerLoop`, `renderJsonResult`, `formatJsonDocument`,
1446
- `aguiConformanceReport`) and the closed name patterns and vocabularies (`RUNTIME_TAG`,
1447
- `SKILL_NAME`, `EFFORTS`).
1463
+ `aguiConformanceReport`, `requestOrigin` for {§http-request-origin}) and the closed name patterns
1464
+ and vocabularies (`RUNTIME_TAG`, `SKILL_NAME`, `EFFORTS`).
1448
1465
 
1449
1466
  §parser-construction-boundary Parser construction components are internal rather
1450
1467
  than alternate consumer entry points:
@@ -1678,7 +1695,8 @@ authority, and optional local `cardPath`, `headers`, and bearer
1678
1695
 
1679
1696
  `ApplicationPort` is the single transport-neutral TypeScript contract through
1680
1697
  which an exterior adapter drives and observes the Plurnk application. Core
1681
- implements it; AG-UI, A2A, and other interface modules consume it. The port
1698
+ implements it; AG-UI, A2A, and other interface modules consume it from
1699
+ `@plurnk/plurnk-contracts/port` ({§runtime-neutral-root}). The port
1682
1700
  contains typed application calls and a scoped event subscription, not wire
1683
1701
  route names, protocol framing, persistence access, or adapter-specific methods.
1684
1702
  An exterior adapter owns its own protocol validation, identity binding, and
@@ -1700,13 +1718,13 @@ it; Core validates and records it through the same prompt admission path.
1700
1718
  `runLoop.attachments` carries typed bytes selected by the adapter;
1701
1719
  `runLoop.envelope` retains opaque protocol evidence under
1702
1720
  {§message-envelope-evidence}. `readMessages` projects durable inbox messages
1703
- and successful conversation replies independently of log visibility, retaining
1721
+ and successful replies independently of log visibility, retaining
1704
1722
  each reply's `answers` addresses (empty for incoming messages).
1705
1723
  `resolveClientInteraction` may carry the accepted answer's message evidence;
1706
1724
  Core validates the resolution, retains the arrival, then resumes the operation.
1707
1725
 
1708
1726
  §application-worker-observation Worker observation exposes durable identity,
1709
- origin, immediate parent identity, minted `kind` (`conversation`; `fork` for a
1727
+ origin, immediate parent identity, minted `kind` (`root` for a worker with no parent; `fork` for a
1710
1728
  child carrying a fork boundary; `work` for any other child), and `lifecycle`,
1711
1729
  the worker's representative work loop projected through {§loop-lifecycle-vocabulary} (`idle`
1712
1730
  when it has none). `selectWorkerLoop` chooses running before parked before queued;
@@ -1729,7 +1747,11 @@ in `@plurnk/plurnk-contracts` is that projection's one owner.
1729
1747
 
1730
1748
  §application-loop-observation Loop observation exposes the durable scheduler
1731
1749
  state of work loops (excluding maintenance-only administrative loops), nullable
1732
- `waitUntil` (the durable observation deadline in Unix milliseconds), exact terminal `OperationResult`, and exact count of packet-bearing
1750
+ `waitUntil` (the durable observation deadline in Unix milliseconds), the times a loop first
1751
+ ran (`claimedAt`, {§loop-claim-latency}) and concluded (`terminatedAt`), ISO 8601 UTC and null
1752
+ until they happen, exact terminal `OperationResult`, the operation that opened the loop
1753
+ (`invocation`: its Worker and log address, {§worker-obligations}; null when no Worker's operation did),
1754
+ and exact count of packet-bearing
1733
1755
  Turns for one owned Worker. Packetless producer Turns and physical provider
1734
1756
  retries do not contribute to `packetCount`. Exterior
1735
1757
  adapters consume this projection instead of reconstructing lifecycle from
@@ -1756,15 +1778,15 @@ class PlurnkParseError extends Error {
1756
1778
  ```
1757
1779
 
1758
1780
  §parse-admission **A diagnostic's severity does not describe whether an operation ran.**
1759
- `operationOmitted` identifies unexecuted unfenced operations, unknown operation-shaped
1760
- tags and unrecovered native calls. A warning about a retained operation or a quoted
1781
+ `operationOmitted` identifies unexecuted unfenced operations and unrecovered native
1782
+ calls. A warning about a retained operation or a quoted
1761
1783
  example does not set it. This fact survives serialization when true; consumers do
1762
1784
  not infer it from diagnostic wording. It does not independently prescribe a strike.
1763
1785
 
1764
1786
  §parse-recovery **Every hard diagnostic carries its working form.** A `severity: "error"`
1765
1787
  diagnostic names, in `recovery`, the form of the construct it refused, in the model's terms: a
1766
1788
  refused heading carries the operation's canonical line (`` `READ (path) <L,M>? pattern? <!-- aside -->?`
1767
- on the opening fence line; READ takes no body. ``), a refused scope the scope's form, and a refused
1789
+ on the opening fence line. ``), a refused scope the scope's form, and a refused
1768
1790
  matcher its dialect's form with an example (`` A pattern is a regex written `/pattern/flags`, such
1769
1791
  as `/timeout/i`. ``). The working form is the language's, never a reconstruction of the input
1770
1792
  ({§diagnostic-observation}). The runtime projects `recovery` as the Problem's `recovery` beside the
@@ -1853,7 +1875,7 @@ diagnostics are:
1853
1875
  that already names a channel keeps such a token as its pattern. Those
1854
1876
  matchers remain independent of owner options: a block without `pattern` never
1855
1877
  erases the heading matcher, and invalid blocks still reach the owning validator.
1856
- These operations take no body, so heading-line text can mean nothing else. On EDIT only
1878
+ Their heading-line text owns selection independently of annotation or distillation bodies. On EDIT only
1857
1879
  a sigil lifts, because plain heading-line text is the replacement body it always
1858
1880
  was; the lines beneath the heading are then the replacement, and none deletes each
1859
1881
  match ({§edit-pattern}). A trailing `<!-- aside -->` on the same line stays the
@@ -1914,15 +1936,12 @@ diagnostics are:
1914
1936
  body instead, the anchor would be written into the file, or the EDIT refused for want of a line
1915
1937
  marker (14 distinct recorded headings). On READ and KILL the same text stays a literal matcher,
1916
1938
  since `@patch` is a search a model means.
1917
- - §matcher-body-redirect **Unread body text on bodyless operations.** FIND, READ and bodyless
1918
- KILL keep the statement but do not use text the matcher did not consume, with one warning
1919
- naming where it stands: `READ takes no body; lines 4–38 were not used.`, or `the text after its
1920
- heading on line 3` when it shares the heading's line. KILL's advisory says `This KILL`, limiting the claim to that invocation:
1921
- log distillation bodies remain valid. Delivered like
1922
- {§misplaced-aside-advisory} as a `parse_advisory` notice (a warning, never an error).
1923
- One sigil line beneath a bodyless heading is the bare form written a line low and still lifts;
1924
- nothing else is promoted into a matcher from below the heading. An option block's `pattern`
1925
- prevents that lift, and the advisory never echoes the discarded text.
1939
+ - §matcher-body-redirect **Unread selector text.** An option block's `pattern` owns the
1940
+ selector; surplus heading text is unused with one warning naming its location.
1941
+ A non-log KILL's body is likewise unused: `This KILL takes no body; line 2 was not used.`
1942
+ Neither warning echoes discarded text. No operation recovers a matcher from below
1943
+ its header. READ/FIND bodies follow {§retrieval-annotation}; log KILL bodies follow
1944
+ {§log-kill-distillation}.
1926
1945
  - §combined-anchor-tolerance **Combined line number and anchor in a scope.** A
1927
1946
  text-coordinate scope written `L<@hash>` — digits immediately before the opener — is the
1928
1947
  displayed `42<@abcde>` row prefix copied whole: the scope is the anchor, the number is dropped, and one
@@ -1938,15 +1957,6 @@ diagnostics are:
1938
1957
  states the anchor rule instead, with the scope marked: `` every line anchor carries
1939
1958
  its `@`: `<@abcde,@fghij>` ``. Spacing and boundary-loss diagnostics retain their own
1940
1959
  contracts.
1941
- - §misplaced-aside-advisory **Aside in the body.** A READ or FIND whose
1942
- body is solely an HTML comment (`<!-- … -->`) can never carry a matcher: it is
1943
- the aside the model put on the line below the heading. The builder takes
1944
- the comment as the aside when the heading has none, builds the operation
1945
- with no body, and raises one warning-severity advisory stating that observed
1946
- normalization; the parser places the advisory right after its statement and
1947
- the service delivers it as a `parse_advisory` notice with its position. A body
1948
- with any other content is ignored under the same advisory path
1949
- ({§matcher-body-redirect}).
1950
1960
 
1951
1961
  §error-shape The diagnostic class determines the parts of a message; every class says what
1952
1962
  happened, never why ({§diagnostic-observation}). Advisories belong to one successfully built statement. A rejected
@@ -1969,6 +1979,11 @@ intent ({§diagnostic-observation}).
1969
1979
  Unexpected top-level text immediately after a closed operation identifies that
1970
1980
  operation's opening line, closing line, and matching backtick count.
1971
1981
 
1982
+ §parse-attempt A hard fact that refused a statement the parser opened carries its `attempt`: the
1983
+ statement's text exactly as written, from its opening fence as far as the parser read it, and the
1984
+ `verb` that fence names, an operation keyword or an execution's runtime tag. A diagnostic that refused no opened
1985
+ statement, such as a lost boundary or a stray character outside every statement, carries none.
1986
+
1972
1987
  Examples of canonical hard facts:
1973
1988
 
1974
1989
  - `unrecognized character '<' in target`