seatmesh 1.0.2 → 1.0.3

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 (49) hide show
  1. package/dist/commands/ack-cli.d.ts +4 -0
  2. package/dist/commands/ack-cli.d.ts.map +1 -0
  3. package/dist/commands/ack-cli.js +105 -0
  4. package/dist/commands/ack-cli.js.map +1 -0
  5. package/dist/commands/checkback-cli.d.ts.map +1 -1
  6. package/dist/commands/checkback-cli.js +1 -0
  7. package/dist/commands/checkback-cli.js.map +1 -1
  8. package/dist/commands/notify-cli.d.ts.map +1 -1
  9. package/dist/commands/notify-cli.js +69 -10
  10. package/dist/commands/notify-cli.js.map +1 -1
  11. package/dist/commands/target-cli.d.ts +4 -0
  12. package/dist/commands/target-cli.d.ts.map +1 -0
  13. package/dist/commands/target-cli.js +146 -0
  14. package/dist/commands/target-cli.js.map +1 -0
  15. package/dist/main.js +241 -77
  16. package/dist/main.js.map +1 -1
  17. package/dist/setup/agent-context-init.d.ts.map +1 -1
  18. package/dist/setup/agent-context-init.js +11 -5
  19. package/dist/setup/agent-context-init.js.map +1 -1
  20. package/dist/setup/init.d.ts +23 -3
  21. package/dist/setup/init.d.ts.map +1 -1
  22. package/dist/setup/init.js +71 -3
  23. package/dist/setup/init.js.map +1 -1
  24. package/dist/setup/update.d.ts.map +1 -1
  25. package/dist/setup/update.js +17 -11
  26. package/dist/setup/update.js.map +1 -1
  27. package/dist/ui/banner.d.ts +17 -2
  28. package/dist/ui/banner.d.ts.map +1 -1
  29. package/dist/ui/banner.js +76 -2
  30. package/dist/ui/banner.js.map +1 -1
  31. package/dist/ui/banner.test.d.ts +2 -0
  32. package/dist/ui/banner.test.d.ts.map +1 -0
  33. package/dist/ui/banner.test.js +47 -0
  34. package/dist/ui/banner.test.js.map +1 -0
  35. package/dist/ui/version-nudge.d.ts +16 -2
  36. package/dist/ui/version-nudge.d.ts.map +1 -1
  37. package/dist/ui/version-nudge.js +80 -3
  38. package/dist/ui/version-nudge.js.map +1 -1
  39. package/dist/ui/version-nudge.test.js +31 -3
  40. package/dist/ui/version-nudge.test.js.map +1 -1
  41. package/package.json +1 -1
  42. package/profiles/minimal/roles/common.yaml +3 -0
  43. package/templates/init/AGENTS.md +87 -0
  44. package/templates/init/mesh.config.yaml +4 -4
  45. package/templates/init/roles/common.yaml +4 -1
  46. package/templates/init/roles/manager.yaml +3 -3
  47. package/templates/init/roles/mini.yaml +4 -4
  48. package/templates/init/roles/secretary.yaml +2 -2
  49. package/templates/init/roles/worker.yaml +5 -5
package/dist/main.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { findDotSmConfig } from "@seat-mesh/core";
4
- import { loadProfile, profilePaths, loadRoleIndex, renderRoleIndex, validateRoleIndex, runStackPassthrough, roleAllows, isCoordKind, isManagerKind, managerColumnIds, addBaseColumn, removeBaseColumn, listBaseColumns, } from "@seat-mesh/core";
4
+ import { loadProfile, profilePaths, loadRoleIndex, renderRoleIndex, validateRoleIndex, runStackPassthrough, roleAllows, isCoordKind, isManagerKind, managerColumnIds, addBaseColumn, removeBaseColumn, listBaseColumns, chatRoomConfigForLoaded, clearAckEndedSync, } from "@seat-mesh/core";
5
5
  import { snapshotConnectivity, formatStatus } from "@seat-mesh/connectivity";
6
6
  import { createRegistryForProfile } from "@seat-mesh/providers";
7
- import { printWhoami, whoamiJson, whoamiJsonWithValidate, validateWhoamiRoleIndex, printAgentCard, printAgentContext, printAgentContextAllRoles, runWhoami, roleKindFromWhoami, requireCoordRole, requireInboxLifecycleRole, capturePaneSnapshot, listSessionPanes, sessionAttach, sessionUp, sessionStatus, relayoutMeshSession, realignAllLayouts, reloadMesh, ensureMeshInbox, startMeshInbox, stopMeshInbox, restartMeshInbox, meshInboxEverConfigured, meshInboxPort, printMeshInboxStatus, waitForMeshInbox, printInboxMeta, tailInboxLog, listInboxInstances, parseInboxStatusFlags, inboxStatusArgv, listInbox, resolveInbox, secretaryLaunch, secretaryRestart, secretaryDispatch, secretaryCollect, secretaryMeshWatch, secretarySupervise, secretaryStatus, printMiniList, miniSpawn, miniPrompt, miniDone, miniSpawnAll, runMeshSmoke, printSmokeResults, launchSession, printLaunchResults, enqueuePrompt, injectPromptDirect, printPeerVerify, runPeerVerify, runRemind, printRemindResults, runNight, runContinue, printContinueResults, runSlotAdvice, verifyMeshSession, printVerify, labelMeshSession, liveMeshSession, ensureBaseLayout, realignBaseLayout, applyMeshSessionBorders, runFlush, printFlushResults, runSwitch, runSet, runTag, setPaneTitle, setPaneStatus, printSeatContexts, runPeek, runPaneMeta, runPpa, runToSlot, runToMini, runPeer, parseRemotePeerTarget, runRemotePeer, applyMeshState, saveMeshSession, saveMeshSessionDetailed, assertSaveAllowed, formatSaveSummary, submitPaneOp, clearPaneOpsQueue, printPaneOpsList, printRelayoutPlan, assertRelayoutSafe, buildFullColdStartBrief, enqueueColdStart, runSeatInit, applyAgentContractBundle, balanceLeadCommand, } from "@seat-mesh/tmux";
7
+ import { printWhoami, whoamiJson, whoamiJsonWithValidate, validateWhoamiRoleIndex, printAgentCard, printAgentContext, printAgentContextAllRoles, runWhoami, roleKindFromWhoami, requireCoordRole, requireInboxLifecycleRole, assertAgentDispatch, printAgentUnauthorized, stripAgentFromArgv, AGENT_META, capturePaneSnapshot, listSessionPanes, sessionAttach, sessionUp, sessionStatus, relayoutMeshSession, realignAllLayouts, reloadMesh, ensureMeshInbox, startMeshInbox, stopMeshInbox, restartMeshInbox, meshInboxEverConfigured, meshInboxPort, printMeshInboxStatus, waitForMeshInbox, printInboxMeta, tailInboxLog, listInboxInstances, parseInboxStatusFlags, inboxStatusArgv, listInbox, resolveInbox, secretaryLaunch, secretaryRestart, secretaryDispatch, secretaryCollect, secretaryMeshWatch, secretarySupervise, secretaryStatus, printMiniList, miniSpawn, miniPrompt, miniDone, miniSpawnAll, runMeshSmoke, printSmokeResults, launchSession, printLaunchResults, enqueuePrompt, injectPromptDirect, printPeerVerify, runPeerVerify, runRemind, printRemindResults, runNight, runContinue, printContinueResults, runSlotAdvice, verifyMeshSession, printVerify, labelMeshSession, liveMeshSession, ensureBaseLayout, realignBaseLayout, applyMeshSessionBorders, runFlush, printFlushResults, runSwitch, runSet, runTag, setPaneTitle, setPaneStatus, printSeatContexts, runPeek, runPaneKind, runPaneMeta, runPpa, runToSlot, runToMini, runPeer, parseRemotePeerTarget, runRemotePeer, applyMeshState, saveMeshSession, saveMeshSessionDetailed, assertSaveAllowed, formatSaveSummary, submitPaneOp, clearPaneOpsQueue, printPaneOpsList, printRelayoutPlan, assertRelayoutSafe, buildFullColdStartBrief, enqueueColdStart, runSeatInit, applyAgentContractBundle, balanceLeadCommand, } from "@seat-mesh/tmux";
8
8
  import { parseAgentApplyArgs } from "@seat-mesh/core";
9
9
  import { buildChatCommands } from "./commands/chat-cli.js";
10
10
  import { buildCheckbackCommands } from "./commands/checkback-cli.js";
11
+ import { buildTargetCommands } from "./commands/target-cli.js";
12
+ import { buildAckCommands } from "./commands/ack-cli.js";
11
13
  import { buildNotifyCommand } from "./commands/notify-cli.js";
12
14
  import { buildPreviewCommand } from "./commands/preview-cli.js";
13
15
  import { buildContractLockCommands } from "./commands/contract-lock-cli.js";
@@ -46,9 +48,11 @@ function usage(loaded) {
46
48
  Setup (run once per project, by a human)
47
49
  start init (if no .sm/ here) + session up, in one step
48
50
  init [--force] [--seats-root PATH] [--name NAME] create .sm/ dotdir
51
+ session init <sm-name> [--force] [--name NAME] create .sm-<name>/ (multi-config)
49
52
  sessions [pick|list|attach|forget|register] [--json] global registry + TUI picker
50
53
  update [--dry-run] [--migrate] [--no-restart-inbox]
51
54
  refresh _vendor (file-by-file) + paths.json; restart inbox
55
+ version [--json] [--check-registry] CLI vs npm latest vs profile .seatmesh-version
52
56
 
53
57
  Status / diagnostics
54
58
  report [--json] [--verbose] stack status (one line default; --verbose = full section dump)
@@ -56,7 +60,7 @@ Status / diagnostics
56
60
  test smoke: layout, providers, inbox, proxy
57
61
 
58
62
  Session / layout (infra shape, operator or manager)
59
- session attach|up|status
63
+ session attach|up|status|init <sm-name>
60
64
  reload [--layout] rebuild engine + labels (no session kill; --layout re-grids)
61
65
  layout [--no-leads] [--dry-run] [--yes] workers + minis grid (queued)
62
66
  layout column list|add <id> [--cli P] [--after ID] [--co-typed]|remove <id>
@@ -67,70 +71,95 @@ Session / layout (infra shape, operator or manager)
67
71
  labels re-apply @mesh_* + border strip
68
72
  inbox [--json] [--wait N] [--meta] | inbox list|resolve|log|instances|stop|restart
69
73
 
70
- Agent runtime (what a pane calls on itself, every turn)
71
- whoami [target] [--validate] | cold-start [--inject] | seat init|now|assign|mark|task|remind
72
- agent [target] scoped can/cannot for this pane (profile role)
73
- agent context [roles|init] registered read_first/files; init scaffolds .sm + validates
74
- func <id> <args...> attached external (funcs: in mesh.config.yaml)
75
- contexts [--json] | peek | pane-meta | ppa
76
- switch | handoff | set | tag <target|self> <id|--auto> | title | status
77
- night on|off|status | continue <slot|all> (manager + night on)
78
- slot-advice <slot> [--send] [note...] (manager; worktree/DB/Redis heuristics)
79
- prompt | remind | flush
74
+ Agent runtime (pane — always via gateway)
75
+ agent scoped can/cannot for this pane
76
+ agent <cmd> … run <cmd> if allowed; else UNAUTHORIZED
77
+ agent context [roles|init] registered read_first/files; init scaffolds .sm
78
+ agent apply|preflight … contract apply
80
79
 
81
- Coordination / comms (pane to pane)
82
- assign <target> <text...> FOCUS NOW + TASK + peer SENT (do not hand-edit FOCUS)
83
- peer <target> <msg...> FYI/ACK only — work goes through assign
84
- to-slot | to-mini <msg...> enqueue (daemon injects) — to-master retired, use room say
85
- secretary start|dispatch|collect|status|watch …
86
- mini list|spawn|prompt|done|dispatch-all
87
- checkback start|list|cancel|cancel-all|reset|ack (alias: patience)
88
- coord expect <target> <hub> <snippet...> arm coord-expect on manager (verify + re-assign)
89
- room | chat | index | proxy | providers | manager | stack | profile show
90
- notify <session> <check> [--url URL] desktop toast (seat from TMUX pane)
91
- notify yesno <title> <body> [--target secretary] Yes/No links -> peer (notify-act)
92
- preview <file...> [--set days] [--notify] publish markdown to mdview.io
93
- launch [--now] [targets…]
80
+ Shared/operator (outside agent — humans + session ops)
81
+ session | update | init | report | test | layout | save | inbox restart | target …
94
82
 
95
- --profile <dir|yaml> override config (default: .sm/ walk-up or bundled profile)
83
+ Examples: agent whoami | agent kind slot-1 | agent peer <t> "<msg>" | agent ack <id> "<note>" | agent cb list
84
+ Operator EOD: target add "finish s13 tickets" [--deadline eod|6h] · target list · target done <id> · target triage <id>
85
+
86
+ --profile <dir|yaml> optional; default walk-up .sm/; multi-config: --profile .sm-<name>
96
87
  `);
97
88
  }
89
+ function printPlainUsage(profileArg) {
90
+ if (profileArg) {
91
+ try {
92
+ usage(meshLoaded(profileArg));
93
+ return;
94
+ }
95
+ catch {
96
+ /* fall through — plain help without profile load */
97
+ }
98
+ }
99
+ usage();
100
+ }
98
101
  async function main() {
99
- const { maybePrintVersionNudge } = await import("./ui/version-nudge.js");
100
- maybePrintVersionNudge();
101
102
  const { profile: profileArg, rest } = parseArgs(process.argv.slice(2));
102
103
  const [cmd, sub, ...tail] = rest;
103
- if (!cmd || (cmd.startsWith("-") && cmd !== "-h" && cmd !== "--help")) {
104
- const json = rest.includes("--json");
105
- if (!profileArg && !findDotSmConfig()) {
106
- const { printDiscoveryReport } = await import("./report/discovery-report.js");
107
- const report = await printDiscoveryReport({ json });
108
- process.exit(report.ok ? 0 : 1);
104
+ // Bare `npx seatmesh` / help: logo+version once per terminal; later invocations plain usage.
105
+ const helpCmd = cmd === "-h" || cmd === "--help" || cmd === "help";
106
+ const blankCmd = !cmd || (cmd.startsWith("-") && !helpCmd);
107
+ if (blankCmd || helpCmd) {
108
+ const { printSeatmeshBanner } = await import("./ui/banner.js");
109
+ const { formatHelpVersionBlock, installKindLabel, readInstalledCliVersion, resolveLatestRegistryVersion, } = await import("./ui/version-nudge.js");
110
+ const showedBrand = printSeatmeshBanner({ tagline: true });
111
+ if (showedBrand) {
112
+ const installed = readInstalledCliVersion();
113
+ const latest = resolveLatestRegistryVersion(false);
114
+ for (const line of formatHelpVersionBlock(installed, latest, installKindLabel())) {
115
+ console.log(line);
116
+ }
117
+ console.log("");
109
118
  }
110
- const loaded = meshLoaded(profileArg);
111
- const { printStatusReport } = await import("./report/status-report.js");
112
- const verbose = rest.includes("--verbose") || rest.includes("-v");
113
- const report = await printStatusReport(loaded, { json, verbose });
114
- process.exit(report.ok ? 0 : 1);
119
+ printPlainUsage(profileArg);
120
+ return;
115
121
  }
116
- if (cmd === "-h" || cmd === "--help" || cmd === "help") {
122
+ const { maybePrintVersionNudge } = await import("./ui/version-nudge.js");
123
+ maybePrintVersionNudge();
124
+ if (cmd === "version") {
125
+ const { printVersionInfo } = await import("./ui/version-nudge.js");
126
+ let profileDir;
117
127
  try {
118
- usage(profileArg ? meshLoaded(profileArg) : undefined);
128
+ profileDir = meshLoaded(profileArg).profileDir;
119
129
  }
120
130
  catch {
121
- usage();
131
+ /* no profile — CLI only */
122
132
  }
133
+ printVersionInfo({
134
+ profileDir,
135
+ json: rest.includes("--json"),
136
+ forceRegistryCheck: rest.includes("--check-registry"),
137
+ });
123
138
  return;
124
139
  }
125
140
  if (cmd === "update") {
126
141
  const { runUpdate } = await import("./setup/update.js");
127
- const { isNpxEphemeralInstall } = await import("./ui/version-nudge.js");
142
+ const { formatInstallStatusLine, isNpxEphemeralInstall, readInstalledCliVersion, resolveLatestRegistryVersion, semverLess, } = await import("./ui/version-nudge.js");
128
143
  const dryRun = rest.includes("--dry-run");
129
144
  const migrate = rest.includes("--migrate");
130
145
  const noRestartInbox = rest.includes("--no-restart-inbox");
146
+ const installed = readInstalledCliVersion();
147
+ const npmLatest = resolveLatestRegistryVersion(true);
148
+ console.log(formatInstallStatusLine(installed, npmLatest));
131
149
  const r = runUpdate({ profileArg, dryRun, migrate });
132
150
  console.log(`OK: update dryRun=${dryRun} migrate=${migrate}`);
133
- console.log(` package: ${r.packageVersion}${r.previousVersion ? ` (was ${r.previousVersion})` : ""}`);
151
+ console.log(` CLI running: ${r.packageVersion}`);
152
+ if (r.previousVersion != null) {
153
+ console.log(` profile .seatmesh-version: ${r.previousVersion} (last profile update — not npm "installed")`);
154
+ }
155
+ else {
156
+ console.log(` profile .seatmesh-version: (none yet)`);
157
+ }
158
+ if (npmLatest)
159
+ console.log(` npm latest: ${npmLatest}`);
160
+ if (npmLatest && semverLess(installed, npmLatest)) {
161
+ console.error(` WARN: CLI ${installed} < npm ${npmLatest} — upgrade: npm install -g seatmesh@latest or npx seatmesh@latest update`);
162
+ }
134
163
  console.log(` paths: ${r.pathsManifest}`);
135
164
  for (const line of r.refreshed)
136
165
  console.log(` refreshed: ${line}`);
@@ -214,7 +243,7 @@ async function main() {
214
243
  catch {
215
244
  /* non-fatal */
216
245
  }
217
- console.log(" next: npx seatmesh session up (or ./sm.sh if wired)");
246
+ console.log(" next: seatmesh session up");
218
247
  return;
219
248
  }
220
249
  if (cmd === "sessions") {
@@ -237,6 +266,37 @@ async function main() {
237
266
  return;
238
267
  }
239
268
  if (cmd === "session") {
269
+ if (sub === "init") {
270
+ const { normalizeSmProfileDir } = await import("@seat-mesh/core");
271
+ const rawName = tail[0];
272
+ if (!rawName || rawName.startsWith("-")) {
273
+ console.error("usage: session init <sm-name> [--force] [--name NAME]");
274
+ console.error(" creates .sm-<name>/ (default project config is still .sm via: init)");
275
+ console.error(" then: seatmesh --profile .sm-<name> session up");
276
+ process.exit(2);
277
+ }
278
+ const force = tail.includes("--force");
279
+ const nameIdx = tail.indexOf("--name");
280
+ const profileName = nameIdx >= 0 && tail[nameIdx + 1] ? tail[nameIdx + 1] : undefined;
281
+ const smDirName = normalizeSmProfileDir(rawName);
282
+ const r = runInit({ force, name: profileName, smDirName });
283
+ console.log(`OK: session init ${r.smDir}`);
284
+ console.log(` config: ${r.configPath}`);
285
+ console.log(` created: ${r.created.length} file(s)`);
286
+ if (r.skipped.length)
287
+ console.log(` skipped (exists): ${r.skipped.length}`);
288
+ try {
289
+ const { upsertGlobalSession } = await import("@seat-mesh/core");
290
+ await upsertGlobalSession(meshLoaded(r.configPath));
291
+ }
292
+ catch {
293
+ /* non-fatal */
294
+ }
295
+ console.log(` next: seatmesh --profile ${smDirName} session up`);
296
+ console.log(` agents: seatmesh --profile ${smDirName} agent …`);
297
+ console.log(` default .sm/ still needs no --profile (walk-up)`);
298
+ return;
299
+ }
240
300
  const loaded = meshLoaded(profileArg);
241
301
  const touchRegistry = async () => {
242
302
  try {
@@ -263,7 +323,7 @@ async function main() {
263
323
  sessionStatus(loaded);
264
324
  return;
265
325
  }
266
- console.error("usage: session attach|up|status");
326
+ console.error("usage: session attach|up|status|init <sm-name>");
267
327
  process.exit(2);
268
328
  }
269
329
  if (cmd === "verify") {
@@ -462,7 +522,7 @@ async function main() {
462
522
  }
463
523
  if (cmd === "to-master") {
464
524
  // FRAMEWORK-QUEUE #1 (AGENT-FUNC-GUARDS.md): to-master retired — chat-first comms.
465
- console.error('DEPRECATED: use ./sm.sh room say [-r managers] "DONE|BLOCKED|…"');
525
+ console.error('DEPRECATED: use seatmesh --profile .sm agent room say [-r managers] "DONE|BLOCKED|…"');
466
526
  process.exit(2);
467
527
  }
468
528
  if (cmd === "to-slot") {
@@ -512,12 +572,34 @@ async function main() {
512
572
  if (cmd === "peer") {
513
573
  const loaded = meshLoaded(profileArg);
514
574
  let direct = false;
575
+ let endedId;
515
576
  const args = [];
516
- for (const a of [sub, ...tail].filter((x) => x != null && x !== "")) {
517
- if (a === "--direct" || a === "--now")
577
+ const rawArgs = [sub, ...tail].filter((x) => x != null && x !== "");
578
+ for (let i = 0; i < rawArgs.length; i++) {
579
+ const a = rawArgs[i];
580
+ if (a === "--direct" || a === "--now") {
518
581
  direct = true;
519
- else
520
- args.push(a);
582
+ continue;
583
+ }
584
+ if (a === "--ended" || a === "--ack") {
585
+ const id = rawArgs[i + 1];
586
+ if (!id || id.startsWith("-")) {
587
+ console.error("usage: peer … --ended <ack-id>");
588
+ process.exit(2);
589
+ }
590
+ endedId = id;
591
+ i++;
592
+ continue;
593
+ }
594
+ if (a.startsWith("--ended=") || a.startsWith("--ack=")) {
595
+ endedId = a.slice(a.indexOf("=") + 1).trim();
596
+ if (!endedId) {
597
+ console.error("usage: peer … --ended <ack-id>");
598
+ process.exit(2);
599
+ }
600
+ continue;
601
+ }
602
+ args.push(a);
521
603
  }
522
604
  if (args[0] === "verify") {
523
605
  requireCoordRole(loaded, "peer verify");
@@ -531,18 +613,31 @@ async function main() {
531
613
  const [target, ...textParts] = args;
532
614
  const rawMsg = textParts.join(" ").trim();
533
615
  if (!target || !rawMsg) {
534
- console.error("usage: peer verify [target] | peer [--direct] <manager|secretary|slot-N|mini-N|@alias:seat|pane> <msg...>");
616
+ console.error("usage: peer verify [target] | peer [--direct] [--ended <ack-id>] <manager|secretary|slot-N|mini-N|@alias:seat|pane> <msg...>");
535
617
  process.exit(2);
536
618
  }
619
+ const finishEnded = () => {
620
+ if (!endedId)
621
+ return;
622
+ const cfg = chatRoomConfigForLoaded(loaded);
623
+ const res = clearAckEndedSync(cfg.inboxBase, endedId, `peer -> ${target}: ${rawMsg}`);
624
+ if (!res.ok) {
625
+ console.error(`WARN: peer sent but --ended failed: ${res.error ?? "?"}`);
626
+ return;
627
+ }
628
+ console.log(`ok ended ack=${res.id ?? endedId}`);
629
+ };
537
630
  try {
538
631
  const remote = parseRemotePeerTarget(target);
539
632
  if (remote) {
540
633
  runRemotePeer(loaded, remote.alias, remote.seat, rawMsg);
634
+ finishEnded();
541
635
  return;
542
636
  }
543
637
  // Workers + minis: universal peer (to-slot/to-mini/enqueue). Coords keep manager stamp.
544
638
  try {
545
639
  runPeer(loaded, target, rawMsg);
640
+ finishEnded();
546
641
  return;
547
642
  }
548
643
  catch (e) {
@@ -552,7 +647,7 @@ async function main() {
552
647
  requireCoordRole(loaded, "peer");
553
648
  if (direct) {
554
649
  const reg = createRegistryForProfile(loaded.profile);
555
- const { paneId, providerId, token, via } = injectPromptDirect(loaded, reg, target, rawMsg, { manager: true });
650
+ const { paneId, providerId, token, via } = injectPromptDirect(loaded, reg, target, rawMsg, { manager: true, force: true });
556
651
  console.log(`SENT: peer --direct -> ${target} pane=${paneId} provider=${providerId} token=${token ?? "-"} via=${via ?? "direct"}`);
557
652
  }
558
653
  else {
@@ -566,6 +661,7 @@ async function main() {
566
661
  : `SENT: peer -> ${targetLabel} pane=${paneId} token=${token ?? "-"} via=${via ?? "pane-row"}`;
567
662
  console.log(line);
568
663
  }
664
+ finishEnded();
569
665
  }
570
666
  catch (e) {
571
667
  console.error(e.message);
@@ -777,6 +873,21 @@ async function main() {
777
873
  }
778
874
  return;
779
875
  }
876
+ if (cmd === "kind" || cmd === "what" || cmd === "typeof") {
877
+ const loaded = meshLoaded(profileArg);
878
+ const reg = createRegistryForProfile(loaded.profile);
879
+ const args = [sub, ...tail].filter((a) => Boolean(a));
880
+ const json = args.includes("--json");
881
+ const target = args.find((a) => a !== "--json") ?? "here";
882
+ try {
883
+ runPaneKind(loaded, reg, target, { json });
884
+ }
885
+ catch (e) {
886
+ console.error(e.message);
887
+ process.exit(1);
888
+ }
889
+ return;
890
+ }
780
891
  if (cmd === "pane-meta") {
781
892
  const loaded = meshLoaded(profileArg);
782
893
  try {
@@ -1127,28 +1238,46 @@ async function main() {
1127
1238
  if (cmd === "agent") {
1128
1239
  const loaded = meshLoaded(profileArg);
1129
1240
  const reg = createRegistryForProfile(loaded.profile);
1130
- if (sub === "apply") {
1131
- const { bundle, opts } = parseAgentApplyArgs(tail);
1132
- applyAgentContractBundle(loaded, reg, bundle, opts);
1133
- return;
1134
- }
1135
- if (sub === "preflight") {
1136
- const { bundle, opts } = parseAgentApplyArgs(tail);
1137
- applyAgentContractBundle(loaded, reg, bundle, { ...opts, preflight: true, dryRun: true });
1138
- return;
1241
+ if (sub && AGENT_META.has(sub)) {
1242
+ if (sub === "apply") {
1243
+ const { bundle, opts } = parseAgentApplyArgs(tail);
1244
+ applyAgentContractBundle(loaded, reg, bundle, opts);
1245
+ return;
1246
+ }
1247
+ if (sub === "preflight") {
1248
+ const { bundle, opts } = parseAgentApplyArgs(tail);
1249
+ applyAgentContractBundle(loaded, reg, bundle, { ...opts, preflight: true, dryRun: true });
1250
+ return;
1251
+ }
1252
+ if (sub === "context") {
1253
+ if (tail[0] === "init") {
1254
+ const code = runAgentContextInit(loaded, { coldStartInject: tail.includes("--inject") });
1255
+ process.exit(code);
1256
+ }
1257
+ if (tail[0] === "roles") {
1258
+ process.exit(printAgentContextAllRoles(loaded));
1259
+ }
1260
+ const targetArg = tail.find((a) => !a.startsWith("-"));
1261
+ process.exit(printAgentContext(loaded, targetArg));
1262
+ }
1139
1263
  }
1140
- if (sub === "context") {
1141
- if (tail[0] === "init") {
1142
- const code = runAgentContextInit(loaded, { coldStartInject: tail.includes("--inject") });
1143
- process.exit(code);
1264
+ // Gateway: agent <cmd> … → authz then re-enter as top-level <cmd>
1265
+ if (sub && !AGENT_META.has(sub)) {
1266
+ const decision = assertAgentDispatch(loaded, sub, tail);
1267
+ if (decision.kind === "card-target") {
1268
+ const w = runWhoami(loaded, decision.target);
1269
+ printAgentCard(w);
1270
+ return;
1144
1271
  }
1145
- if (tail[0] === "roles") {
1146
- process.exit(printAgentContextAllRoles(loaded));
1272
+ if (decision.kind !== "allow") {
1273
+ printAgentUnauthorized(decision);
1274
+ process.exit(2);
1147
1275
  }
1148
- const targetArg = tail.find((a) => !a.startsWith("-"));
1149
- process.exit(printAgentContext(loaded, targetArg));
1276
+ process.argv = stripAgentFromArgv(process.argv);
1277
+ return main();
1150
1278
  }
1151
- const w = runWhoami(loaded, sub || tail[0]);
1279
+ // Bare `agent` — own card
1280
+ const w = runWhoami(loaded, "here");
1152
1281
  printAgentCard(w);
1153
1282
  return;
1154
1283
  }
@@ -1168,7 +1297,7 @@ async function main() {
1168
1297
  }
1169
1298
  if (loaded.profile.external.default === "deny") {
1170
1299
  console.error(`UNAUTHORIZED: func ${id} denied (external.default=deny)`);
1171
- console.error("hint: ./sm.sh agent");
1300
+ console.error("hint: seatmesh --profile .sm agent");
1172
1301
  process.exit(2);
1173
1302
  }
1174
1303
  try {
@@ -1178,7 +1307,7 @@ async function main() {
1178
1307
  const index = loadRoleIndex(paths.rolesDir, kind);
1179
1308
  if (!roleAllows(index.funcs, id)) {
1180
1309
  console.error(`UNAUTHORIZED: func ${id} denied for role=${kind}`);
1181
- console.error("hint: ./sm.sh agent");
1310
+ console.error("hint: seatmesh --profile .sm agent");
1182
1311
  process.exit(2);
1183
1312
  }
1184
1313
  }
@@ -1225,7 +1354,7 @@ async function main() {
1225
1354
  }
1226
1355
  if (cmd === "whoami" || cmd === "where") {
1227
1356
  if (cmd === "where") {
1228
- console.error("note: where is deprecated — use ./sm.sh whoami");
1357
+ console.error("note: where is deprecated — use seatmesh --profile .sm agent whoami");
1229
1358
  }
1230
1359
  const loaded = meshLoaded(profileArg);
1231
1360
  const json = rest.includes("--json");
@@ -1331,9 +1460,10 @@ async function main() {
1331
1460
  const prov = reg.detect(snap);
1332
1461
  const det = prov?.detect(snap);
1333
1462
  const state = prov?.composerState(snap) ?? { phase: "plain_shell" };
1463
+ const role = snap.options.mesh_role?.trim() || "plain";
1334
1464
  const slot = snap.options.mesh_slot || "-";
1335
1465
  const ports = snap.options.mesh_ports || "-";
1336
- console.log(`${paneId}\t${prov?.id ?? "?"}\t${det?.resumeId ?? "-"}\t${state.phase}${state.limitKind ? `:${state.limitKind}` : ""}\tslot=${slot}\tports=${ports}\t${snap.windowName}`);
1466
+ console.log(`${paneId}\trole=${role}\t${prov?.id ?? "?"}\t${det?.resumeId ?? "-"}\t${state.phase}${state.limitKind ? `:${state.limitKind}` : ""}\tslot=${slot}\tports=${ports}\t${snap.windowName}`);
1337
1467
  }
1338
1468
  return;
1339
1469
  }
@@ -1414,7 +1544,7 @@ async function main() {
1414
1544
  }
1415
1545
  return;
1416
1546
  }
1417
- if (cmd === "checkback" || cmd === "patience") {
1547
+ if (cmd === "checkback" || cmd === "patience" || cmd === "cb") {
1418
1548
  const loaded = meshLoaded(profileArg);
1419
1549
  const getLoaded = () => loaded;
1420
1550
  const branch = buildCheckbackCommands(getLoaded);
@@ -1433,6 +1563,40 @@ async function main() {
1433
1563
  }
1434
1564
  return;
1435
1565
  }
1566
+ if (cmd === "target" || cmd === "targets") {
1567
+ const loaded = meshLoaded(profileArg);
1568
+ const getLoaded = () => loaded;
1569
+ const branch = buildTargetCommands(getLoaded);
1570
+ if (!sub) {
1571
+ branch.outputHelp();
1572
+ return;
1573
+ }
1574
+ try {
1575
+ await branch.parseAsync([sub, ...tail], { from: "user" });
1576
+ }
1577
+ catch (e) {
1578
+ const err = e;
1579
+ if (err.code === "commander.helpDisplayed" || err.code === "commander.version")
1580
+ return;
1581
+ throw e;
1582
+ }
1583
+ return;
1584
+ }
1585
+ if (cmd === "ack") {
1586
+ const loaded = meshLoaded(profileArg);
1587
+ const getLoaded = () => loaded;
1588
+ const branch = buildAckCommands(getLoaded);
1589
+ try {
1590
+ await branch.parseAsync(rest.slice(1), { from: "user" });
1591
+ }
1592
+ catch (e) {
1593
+ const err = e;
1594
+ if (err.code === "commander.helpDisplayed" || err.code === "commander.version")
1595
+ return;
1596
+ throw e;
1597
+ }
1598
+ return;
1599
+ }
1436
1600
  if (cmd === "notify") {
1437
1601
  const loaded = meshLoaded(profileArg);
1438
1602
  const getLoaded = () => loaded;