paseo-room 0.7.0 → 0.8.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 (34) hide show
  1. package/README.md +31 -10
  2. package/dist/index.js +34 -15
  3. package/dist/index.js.map +1 -1
  4. package/dist/prompts/contract/lead.md +22 -2
  5. package/dist/runtime-plugin/client/data.ts +2 -1
  6. package/dist/runtime-plugin/client/effort-settings.tsx +89 -0
  7. package/dist/runtime-plugin/client/model.ts +5 -0
  8. package/dist/runtime-plugin/client/pills.ts +8 -2
  9. package/dist/runtime-plugin/client/record.tsx +8 -0
  10. package/dist/runtime-plugin/client/room.tsx +2 -2
  11. package/dist/runtime-plugin/client/views.tsx +2 -0
  12. package/dist/runtime-plugin/index.server.ts +17 -0
  13. package/dist/runtime-plugin/server/attention/delivery.ts +29 -4
  14. package/dist/runtime-plugin/server/attention/engine.ts +62 -28
  15. package/dist/runtime-plugin/server/attention/mask.ts +6 -0
  16. package/dist/runtime-plugin/server/attention/observer.ts +25 -15
  17. package/dist/runtime-plugin/server/attention/signals.ts +25 -15
  18. package/dist/runtime-plugin/server/attention/triage.ts +41 -2
  19. package/dist/runtime-plugin/server/brief.ts +38 -0
  20. package/dist/runtime-plugin/server/context.ts +22 -2
  21. package/dist/runtime-plugin/server/contracts/actions.ts +4 -0
  22. package/dist/runtime-plugin/server/controller.ts +78 -15
  23. package/dist/runtime-plugin/server/domain/state.ts +20 -2
  24. package/dist/runtime-plugin/server/domain/views.ts +22 -4
  25. package/dist/runtime-plugin/server/events/schema.ts +5 -1
  26. package/dist/runtime-plugin/server/handlers/actions.ts +26 -12
  27. package/dist/runtime-plugin/server/handlers/peer.ts +5 -3
  28. package/dist/runtime-plugin/server/handlers/turns.ts +4 -3
  29. package/dist/runtime-plugin/server/paseo-port.ts +23 -0
  30. package/dist/runtime-plugin/server/rpc.ts +25 -1
  31. package/dist/runtime-plugin/server/tools.ts +30 -8
  32. package/dist/runtime-plugin/shared/effort.ts +27 -0
  33. package/dist/runtime-plugin/shared/rpc-contracts.ts +20 -1
  34. package/package.json +1 -1
package/README.md CHANGED
@@ -483,7 +483,9 @@ vocabulary for Lead and Peer, and exactly one body per role. TypeScript selects
483
483
  that settles it rather than pre-solving the work; any plan or file list in it is provisional.
484
484
  One moving write scope has exactly one owner, and at most one Peer is writable at a time. It
485
485
  explicitly requests native Paseo completion/error/permission notification for Peer creation and
486
- every background follow-up, then waits for events rather than polling. Room tools: on.
486
+ every background follow-up, then waits for events rather than polling. A question only Human can
487
+ answer goes on its own `NEEDS-HUMAN:` line, and an effect beyond the work's intended scope on an
488
+ `INCIDENT:` line, so neither is lost in a long message. Room tools: on.
487
489
  - **Peer** owns one bounded assignment — writable inside an assigned scope, or read-only
488
490
  against a named candidate, question or area — under exactly one disposition Lead names in the
489
491
  brief (Engineer, Architect, Reviewer or Scout), forms its own technical position from the code
@@ -621,10 +623,23 @@ npx paseo-room verify
621
623
  working.
622
624
  - **Lead** gains room tools such as `assignment_create`, `assignment_dispatch`, `assignment_answer`,
623
625
  `assignment_accept`, `gate_run`, and for isolated writers `workspace_close` and `lease_reclaim`.
626
+ `assignment_create` refuses a base that is not a commit of the repository (`base_unknown`).
624
627
  **Supervisor** gains `room_status`, `runtime_findings`, `message_lead` (with a `project` for a
625
- Supervisor of several projects) and `attention_feedback`, and cannot change an assignment.
626
- - **A runtime-dispatched Peer** gets exactly two tools, `ask` and `handoff`, for its own assignment,
627
- and still no Paseo room tools. A report exists only once one of those calls is accepted; its
628
+ Supervisor of several projects) and `attention_feedback`, and cannot change an assignment. Its
629
+ status and findings cover only its portfolio and the project it stands in; `room_status` lists
630
+ the assignments still open or still to close, and only counts settled ones.
631
+ - **Peer thinking.** A runtime-dispatched Peer launches on its room profile's model and thinking
632
+ option. In **Settings › Room seats › Thinking Lead may choose** you can allow other thinking
633
+ options per Peer provider, from the ones Paseo lists for its model. Lead may then pass `thinking`
634
+ (with a `thinkingReason`) to `assignment_dispatch`, as the Lead contract directs. The runtime
635
+ refuses anything outside what you allowed or what the model offers, and always refuses `ultra` and
636
+ `ultracode`, which start agents on their own. It records the choice, shows it with the reason to
637
+ Supervisor and in the panel, and keeps it for a reclaimed Peer while you still allow it. The model
638
+ itself is never Lead's to change.
639
+ - **A runtime-dispatched Peer** is titled `<Disposition> · <outcome gist> · <assignment id>` (for
640
+ example `Reviewer · Review the Docker Compose dev env… · asg_…`), and the runtime's notices to Lead
641
+ name the assignment the same way. It gets exactly two tools, `ask` and `handoff`, for its own
642
+ assignment, and still no Paseo room tools. A report exists only once one of those calls is accepted; its
628
643
  final message is never read as a report. Claude asks for permission before a Peer's first call
629
644
  to `mcp__paseo_room__ask` or `mcp__paseo_room__handoff`: approve it in Paseo, since the runtime
630
645
  never answers a permission for a seat. Codex and Pi Peers do not ask.
@@ -637,8 +652,9 @@ npx paseo-room verify
637
652
  - **The Room runtime panel** (sidebar item, and a workspace panel that opens on its own project)
638
653
  puts what needs you first. Below that come your projects, ordered by status, then your
639
654
  Supervisors.
640
- - A project shows its Supervisor, its Lead and Peer seats (each opens its agent in Paseo) and its
641
- runtime record: assignments, isolated writers, findings and recovery.
655
+ - A project shows its Supervisor, its Lead and Peer seats (each opens its agent in Paseo, and
656
+ shows the model and thinking option it runs with) and its runtime record: assignments, isolated
657
+ writers, findings and recovery.
642
658
  - Starting a Supervisor, starting a project and assigning a Supervisor are guided forms.
643
659
  - The design notes are in [docs/design/runtime-panel-ux.md](docs/design/runtime-panel-ux.md).
644
660
  - **Settings › Room seats** shows which account each seat is signed in to (email, plan and
@@ -659,15 +675,20 @@ learn only when you ask it to check.
659
675
  - They report a permission waiting on any seat for 5 minutes, and a Peer result its idle Lead has
660
676
  not read for 10 minutes.
661
677
  - They report the same failure twice in a row, and possible concurrent writers in one working
662
- tree.
678
+ tree, naming each seat's files and when its turn ended. A write outside that tree does not
679
+ count.
663
680
  - They report a Lead archived while its seats still work, and a Lead's finished turn that the
664
681
  Supervisor did not prompt itself.
682
+ - The Lead contract has Lead put a question for you on a line beginning `NEEDS-HUMAN:` and an
683
+ incident on one beginning `INCIDENT:`. The first wakes the Supervisor and the second pages it,
684
+ even for a turn it prompted, once per line, and the letter quotes those lines rather than the
685
+ message's end.
665
686
  - Letters are held until the Supervisor is idle, batched into digests, and limited to a few wakes
666
687
  an hour. They are never sent while the Supervisor holds a permission, because a send would
667
688
  deny it.
668
- - Each item has an id for `attention_feedback`. A letter is evidence, not an instruction: the
669
- Supervisor contract has it ask or nudge the Lead, or relay a question to you, and never direct a
670
- Peer.
689
+ - Each item has an id for `attention_feedback`, and a letter's own id rates every item in it. A
690
+ letter is evidence, not an instruction: the Supervisor contract has it ask or nudge the Lead, or
691
+ relay a question to you, and never direct a Peer.
671
692
  - **Starting seats.** From the **Room** view, **Start Supervisor** opens one in an existing directory
672
693
  outside every repository. **Start project** checks a repository, opens its Lead under the
673
694
  Supervisor you pick, and sends a fixed kickoff with your first directive verbatim. **Assign
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "paseo-room",
18
- version: "0.7.0",
18
+ version: "0.8.0",
19
19
  description: "One CLI that configures Codex, Claude Code, and Pi role homes in $HOME and registers them with your local Paseo daemon.",
20
20
  keywords: [
21
21
  "paseo",
@@ -691,10 +691,10 @@ async function loginRole(agentValue, roleValue, options, output) {
691
691
  const childEnv = { ...env, ...invocation.env };
692
692
  const launch = options.spawn ?? spawnLogin;
693
693
  return new Promise((resolve5) => {
694
- let settled = false;
694
+ let settled2 = false;
695
695
  const finish = (code2) => {
696
- if (settled) return;
697
- settled = true;
696
+ if (settled2) return;
697
+ settled2 = true;
698
698
  resolve5(code2);
699
699
  };
700
700
  let child;
@@ -1688,20 +1688,20 @@ function runProbeProcess(executable, adapterEntry, env, options, cwd) {
1688
1688
  const child = spawn2(executable, args, { cwd, detached: true, env, stdio: ["pipe", "pipe", "pipe"] });
1689
1689
  const stdout = [];
1690
1690
  let bytes = 0;
1691
- let settled = false;
1691
+ let settled2 = false;
1692
1692
  const finish = (result) => {
1693
- if (settled) return;
1694
- settled = true;
1693
+ if (settled2) return;
1694
+ settled2 = true;
1695
1695
  clearTimeout(timer);
1696
1696
  resolveResult(result);
1697
1697
  };
1698
1698
  const abort = (reason3) => {
1699
- if (settled) return;
1699
+ if (settled2) return;
1700
1700
  terminateProbe(child);
1701
1701
  finish({ ok: false, stdout: Buffer.concat(stdout).toString("utf8"), reason: reason3 });
1702
1702
  };
1703
1703
  const capture = (chunk, keep) => {
1704
- if (settled) return;
1704
+ if (settled2) return;
1705
1705
  bytes += chunk.length;
1706
1706
  if (bytes > outputLimit) {
1707
1707
  abort("overflow");
@@ -2841,6 +2841,7 @@ function reclaimed(view, event) {
2841
2841
  delete copy.peerAgentId;
2842
2842
  delete copy.observedProviderId;
2843
2843
  delete copy.observedModel;
2844
+ delete copy.observedThinking;
2844
2845
  return { ...copy, state: "dispatching", reportingState: "consumed", eventIds: [...view.eventIds, event.id] };
2845
2846
  }
2846
2847
  function applyReport(state, view, event) {
@@ -2923,7 +2924,13 @@ function applyEvent(state, event) {
2923
2924
  const id2 = view.id;
2924
2925
  switch (event.type) {
2925
2926
  case "assignment.dispatch-requested":
2926
- update(state, id2, { state: "dispatching", peerProviderId: event.data.peerProviderId, workspaceId: event.data.workspaceId }, event);
2927
+ update(state, id2, {
2928
+ state: "dispatching",
2929
+ peerProviderId: event.data.peerProviderId,
2930
+ workspaceId: event.data.workspaceId,
2931
+ ...event.data.thinking === void 0 ? {} : { chosenThinking: event.data.thinking },
2932
+ ...event.data.thinkingReason === void 0 ? {} : { thinkingReason: event.data.thinkingReason }
2933
+ }, event);
2927
2934
  return;
2928
2935
  case "ownership.reserved":
2929
2936
  state.ownership.set(id2, { assignmentId: id2, workspaceId: event.data.workspaceId, baseCommit: event.data.baseCommit, state: "reserved" });
@@ -2950,7 +2957,12 @@ function applyEvent(state, event) {
2950
2957
  update(state, id2, { state: "uncertain" }, event);
2951
2958
  return;
2952
2959
  case "binding.published":
2953
- update(state, id2, { observedProviderId: event.data.providerId, observedModel: event.data.model, workspaceId: event.data.workspaceId }, event);
2960
+ update(state, id2, {
2961
+ observedProviderId: event.data.providerId,
2962
+ observedModel: event.data.model,
2963
+ workspaceId: event.data.workspaceId,
2964
+ ...event.data.thinking === void 0 ? {} : { observedThinking: event.data.thinking }
2965
+ }, event);
2954
2966
  return;
2955
2967
  case "ownership.held":
2956
2968
  setOwner(state, id2, { state: "held", agentId: event.data.agentId });
@@ -3281,7 +3293,11 @@ var init_actions = __esm({
3281
3293
  ...assignment,
3282
3294
  peerProvider: z6.string().min(1).max(128),
3283
3295
  isolation: z6.enum(["lead-workspace", "worktree"]).optional(),
3284
- serialOnly: boundedArray(boundedString()).optional()
3296
+ serialOnly: boundedArray(boundedString()).optional(),
3297
+ // A thinking option the operator allows for that Peer provider, and why; omitted, the Peer
3298
+ // launches on its profile's option (docs/design/runtime-coordination-peer-effort.md E-D2).
3299
+ thinking: z6.string().min(1).max(64).optional(),
3300
+ thinkingReason: boundedString(1024).optional()
3285
3301
  }),
3286
3302
  assignment_answer: z6.strictObject({ ...assignment, answer: boundedString() }),
3287
3303
  assignment_rework: z6.strictObject({ ...assignment, instructions: boundedString() }),
@@ -3402,7 +3418,7 @@ function readEvent(value) {
3402
3418
  const parsed2 = readerSchema.safeParse(value);
3403
3419
  return parsed2.success ? { ok: true, event: parsed2.data } : { ok: false, reason: "invalid", detail: z8.prettifyError(parsed2.error) };
3404
3420
  }
3405
- var EVENT_SCHEMA, id, text2, reason2, generation2, digest2, timestamp, workspaceIdSchema, branch, epoch, scopes, gateResultSchema, NOTICE_DISPOSITIONS, NOTICE_CLASSES, EVENT_PAYLOADS, EVENT_TYPES, SUPPORTED_PAYLOAD_VERSIONS, actorSchema, envelope, firstWriter, restWriters, firstReader, restReaders, writerSchema, readerSchema;
3421
+ var EVENT_SCHEMA, id, text2, reason2, thinkingOption, generation2, digest2, timestamp, workspaceIdSchema, branch, epoch, scopes, gateResultSchema, NOTICE_DISPOSITIONS, NOTICE_CLASSES, EVENT_PAYLOADS, EVENT_TYPES, SUPPORTED_PAYLOAD_VERSIONS, actorSchema, envelope, firstWriter, restWriters, firstReader, restReaders, writerSchema, readerSchema;
3406
3422
  var init_schema = __esm({
3407
3423
  "src/runtime-plugin/server/events/schema.ts"() {
3408
3424
  "use strict";
@@ -3415,6 +3431,7 @@ var init_schema = __esm({
3415
3431
  id = z8.string().min(1).max(128);
3416
3432
  text2 = boundedString();
3417
3433
  reason2 = boundedString(1024);
3434
+ thinkingOption = z8.string().min(1).max(64);
3418
3435
  generation2 = z8.number().int().min(1);
3419
3436
  digest2 = z8.string().regex(/^sha256:[0-9a-f]{64}$/);
3420
3437
  timestamp = z8.iso.datetime({ offset: true });
@@ -3449,7 +3466,8 @@ var init_schema = __esm({
3449
3466
  "project.ownership-resolved": { keptLeadAgentId: id, decidedBy: z8.enum(["evidence", "human"]) },
3450
3467
  // Assignment lifecycle decided by Lead or Human (§5.1).
3451
3468
  "assignment.created": { input: assignmentCreateSchema, leadAgentId: id, leadProviderId: id },
3452
- "assignment.dispatch-requested": { peerProviderId: id, workspaceId: id },
3469
+ // Lead's thinking choice within the operator's envelope, and why (peer-effort delta E-D2, E-D4).
3470
+ "assignment.dispatch-requested": { peerProviderId: id, workspaceId: id, thinking: thinkingOption.optional(), thinkingReason: reason2.optional() },
3453
3471
  "assignment.answered": { answer: text2 },
3454
3472
  "assignment.rework-requested": { instructions: text2 },
3455
3473
  "assignment.accepted": {
@@ -3492,7 +3510,8 @@ var init_schema = __esm({
3492
3510
  model: z8.string().min(1).max(256),
3493
3511
  parentAgentId: id,
3494
3512
  workspaceId: id,
3495
- roomGeneration: id
3513
+ roomGeneration: id,
3514
+ thinking: thinkingOption.optional()
3496
3515
  },
3497
3516
  "reporting.generation-opened": { generation: generation2, capabilityHash: digest2, turn: z8.enum(["initial", "answer", "rework", "follow-up"]) },
3498
3517
  "run.requested": { intentId: id, generation: generation2, promptDigest: digest2 },