@agentproto/apps 0.20.3 → 0.21.1

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 (65) hide show
  1. package/dist/agents-overview/panel.mjs +21 -8
  2. package/dist/agents-overview/panel.mjs.map +1 -1
  3. package/dist/agents-overview.mjs +21 -8
  4. package/dist/agents-overview.mjs.map +1 -1
  5. package/dist/bin/sync.mjs +19 -2
  6. package/dist/bin/sync.mjs.map +1 -1
  7. package/dist/bureau-sessions/panel.mjs +21 -8
  8. package/dist/bureau-sessions/panel.mjs.map +1 -1
  9. package/dist/bureau-sessions.mjs +21 -8
  10. package/dist/bureau-sessions.mjs.map +1 -1
  11. package/dist/index.mjs +63 -20
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/live-session/panel.mjs +21 -8
  14. package/dist/live-session/panel.mjs.map +1 -1
  15. package/dist/live-session.mjs +21 -8
  16. package/dist/live-session.mjs.map +1 -1
  17. package/dist/ops-panel/ui.d.ts +1 -1
  18. package/dist/ops-panel/ui.d.ts.map +1 -1
  19. package/dist/ops-panel.mjs +19 -2
  20. package/dist/ops-panel.mjs.map +1 -1
  21. package/dist/panel-bridge.d.ts.map +1 -1
  22. package/dist/review-panel/panel.d.ts +1 -1
  23. package/dist/review-panel/panel.d.ts.map +1 -1
  24. package/dist/review-panel/panel.generated.d.ts +1 -1
  25. package/dist/review-panel/panel.generated.d.ts.map +1 -1
  26. package/dist/review-panel/panel.mjs +1 -1
  27. package/dist/review-panel/panel.mjs.map +1 -1
  28. package/dist/review-panel.mjs +1 -1
  29. package/dist/review-panel.mjs.map +1 -1
  30. package/dist/session-chat/panel.mjs +21 -8
  31. package/dist/session-chat/panel.mjs.map +1 -1
  32. package/dist/session-chat.mjs +21 -8
  33. package/dist/session-chat.mjs.map +1 -1
  34. package/dist/session-story/panel.mjs +21 -8
  35. package/dist/session-story/panel.mjs.map +1 -1
  36. package/dist/session-story.mjs +21 -8
  37. package/dist/session-story.mjs.map +1 -1
  38. package/dist/sessions-panel/panel.mjs +21 -8
  39. package/dist/sessions-panel/panel.mjs.map +1 -1
  40. package/dist/sessions-panel.mjs +21 -8
  41. package/dist/sessions-panel.mjs.map +1 -1
  42. package/dist/store/panel.d.ts +1 -1
  43. package/dist/store/panel.d.ts.map +1 -1
  44. package/dist/store/panel.generated.d.ts +1 -1
  45. package/dist/store/panel.generated.d.ts.map +1 -1
  46. package/dist/store/panel.mjs +1 -1
  47. package/dist/store/panel.mjs.map +1 -1
  48. package/dist/store.mjs +1 -1
  49. package/dist/store.mjs.map +1 -1
  50. package/dist/work-board/panel.d.ts +1 -1
  51. package/dist/work-board/panel.d.ts.map +1 -1
  52. package/dist/work-board/panel.generated.d.ts +1 -1
  53. package/dist/work-board/panel.generated.d.ts.map +1 -1
  54. package/dist/work-board/panel.mjs +21 -8
  55. package/dist/work-board/panel.mjs.map +1 -1
  56. package/dist/work-board.mjs +21 -8
  57. package/dist/work-board.mjs.map +1 -1
  58. package/package.json +3 -3
  59. package/repo-maintenance/.agentproto/workflows/maintain/WORKFLOW.md +17 -1
  60. package/repo-maintenance/.agentproto/workflows/maintain/entry.mjs +88 -5
  61. package/session-steward/.agentproto/workflows/session-steward/WORKFLOW.md +38 -6
  62. package/session-steward/.agentproto/workflows/session-steward/entry.mjs +44 -6
  63. package/session-steward/.agentproto/workflows/session-steward/origin-policy.mjs +2 -2
  64. package/session-steward/README.md +12 -4
  65. package/session-steward/routines/session-steward-hourly/ROUTINE.md +6 -3
@@ -57,6 +57,11 @@ import {
57
57
 
58
58
  const DEFAULT_IDLE_MINUTES = 30
59
59
  const DEFAULT_MIN_CONFIDENCE = 0.8
60
+ /** Recurring (scheduled) runs only: a keepAlive session must have been idle
61
+ * this long before it may be asked, so a session its owner meant to pick up
62
+ * tomorrow is not closed overnight. 0 turns the recurring keepAlive ask off.
63
+ * An on-demand run (`recurring: false`) has no such delay. */
64
+ const DEFAULT_KEEPALIVE_ASK_AFTER_MINUTES = 1440
60
65
  /** Terminal sessions older than this (hours since they ended) are not listed
61
66
  * as relabel candidates. */
62
67
  const DEFAULT_RELABEL_WINDOW_HOURS = 24
@@ -118,6 +123,8 @@ export function resolveSettings(input, modelRoles) {
118
123
  jevModel: typeof i.jevModel === "string" && i.jevModel.trim() ? i.jevModel.trim() : DEFAULT_JEV_MODEL,
119
124
  maxJudged: Math.floor(num(i.maxJudged, DEFAULT_MAX_JUDGED)),
120
125
  askSessions: i.askSessions === true,
126
+ recurring: i.recurring === true,
127
+ keepAliveAskAfterMinutes: num(i.keepAliveAskAfterMinutes, DEFAULT_KEEPALIVE_ASK_AFTER_MINUTES, { min: 0 }),
121
128
  callerSessionId: typeof i.callerSessionId === "string" && i.callerSessionId ? i.callerSessionId : null,
122
129
  callerOrigin: typeof i.callerOrigin === "string" && i.callerOrigin ? i.callerOrigin : null,
123
130
  appId: typeof i.appId === "string" ? i.appId.trim() : DEFAULT_APP_ID,
@@ -427,8 +434,33 @@ export function collectVerdicts(evidenceResult, judgeQueue, jevResult, jevQueue,
427
434
 
428
435
  // ── ask (opt-in) ─────────────────────────────────────────────────────────
429
436
 
430
- /** Sessions to ask directly: judged below `minConfidence`, and idle, not
431
- * keepAlive, not awaitingInput — only when `askSessions`. */
437
+ /** Nothing to lose in the session's worktree: it is merged (the plan's signal
438
+ * or the PR state), or it is a linked worktree with no uncommitted changes
439
+ * and no commits beyond the base. No worktree info at all is unknown, not
440
+ * clean — a session in a plain checkout stays out. */
441
+ export function worktreeHasNothingToLose(evidence) {
442
+ if (evidence?.signals?.worktreeMerged === true) return true
443
+ const wt = evidence?.worktree
444
+ if (!wt) return false
445
+ if (wt.pr?.state === "merged") return true
446
+ return wt.dirty === false && wt.ahead === 0
447
+ }
448
+
449
+ /** keepAlive is the restart-relight flag; closing is a separate, active
450
+ * decision. A keepAlive session whose worktree holds nothing to lose may be
451
+ * asked. An on-demand run (a human asked for it) applies no idle delay beyond
452
+ * the plan's own `idleMinutes`. A `recurring` run closes things nobody asked
453
+ * about, so it also waits `keepAliveAskAfterMinutes` of idleness (0 = never). */
454
+ export function keepAliveAskEligible(evidence, settings) {
455
+ if (evidence?.keepAlive !== true || !worktreeHasNothingToLose(evidence)) return false
456
+ if (settings.recurring !== true) return true
457
+ const after = settings.keepAliveAskAfterMinutes
458
+ return after > 0 && typeof evidence.idleMinutes === "number" && evidence.idleMinutes >= after
459
+ }
460
+
461
+ /** Sessions to ask directly: judged below `minConfidence`, not awaitingInput
462
+ * or busy, and either not keepAlive or a keepAlive passing
463
+ * {@link keepAliveAskEligible} — only when `askSessions`. */
432
464
  export function buildAskQueue(verdicts, settings) {
433
465
  if (!settings?.askSessions) return []
434
466
  return (verdicts ?? [])
@@ -436,7 +468,7 @@ export function buildAskQueue(verdicts, settings) {
436
468
  r =>
437
469
  r.evidence &&
438
470
  r.confidence < settings.minConfidence &&
439
- r.evidence.keepAlive !== true &&
471
+ (r.evidence.keepAlive !== true || keepAliveAskEligible(r.evidence, settings)) &&
440
472
  r.evidence.awaitingInput !== true &&
441
473
  r.evidence.busy !== true,
442
474
  )
@@ -995,12 +1027,14 @@ export default {
995
1027
  judgeModel: { type: "string", description: `Model for the agent judge. Default: the \`${ROLE_JUDGE_SESSION}\` model role (repo agentproto.json \`models\` > daemon config \`models\` > built-in).` },
996
1028
  maxJudged: { type: "number", description: `Most \`judge\` sessions judged per run, most RAM first. Default ${DEFAULT_MAX_JUDGED}.`, default: DEFAULT_MAX_JUDGED },
997
1029
  askSessions: { type: "boolean", description: "Ask low-confidence idle sessions directly whether they're done. Default false — it spends a turn in someone else's conversation.", default: false },
1030
+ recurring: { type: "boolean", description: "Set by scheduled runs (the hourly routine). A recurring run closes sessions nobody asked about, so a keepAlive session must also have been idle `keepAliveAskAfterMinutes`. Default false = an on-demand run, which applies no such delay.", default: false },
1031
+ keepAliveAskAfterMinutes: { type: "number", description: `With \`askSessions\`, for \`recurring\` runs only: a keepAlive session whose worktree is merged or clean (nothing uncommitted, nothing ahead of base) is asked only once idle this many minutes; 0 disables the recurring keepAlive ask. On-demand runs ignore it. keepAlive only re-lights a session after a daemon restart; it does not stop a declared DONE from closing it. Default ${DEFAULT_KEEPALIVE_ASK_AFTER_MINUTES}.`, default: DEFAULT_KEEPALIVE_ASK_AFTER_MINUTES },
998
1032
  callerSessionId: { type: "string", description: "The calling session's id — never a candidate. The CLI passes AGENTPROTO_SESSION_ID." },
999
1033
  callerOrigin: { type: "string", description: "The calling session's origin (`cron:<jobId>`) — an older run of the SAME cron job is never judged as user work." },
1000
1034
  appId: { type: "string", description: `Installed app whose \`app_state\` ledger holds the verdict memory. Default ${DEFAULT_APP_ID}.` },
1001
1035
  stableVerdictPasses: { type: "number", description: `Consecutive passes on an unchanged evidence fingerprint before the judge cache stops re-judging. Default ${DEFAULT_STABLE_VERDICT_PASSES}.`, default: DEFAULT_STABLE_VERDICT_PASSES },
1002
1036
  userOrigins: { type: "array", description: `Origins that are ALWAYS flag-only, never closed (a human is in the loop). Trailing \`*\` is a prefix wildcard. Default ${JSON.stringify(DEFAULT_USER_ORIGINS)}.`, items: { type: "string" }, default: DEFAULT_USER_ORIGINS },
1003
- closableOrigins: { type: "array", description: `Origins that may be closed under the current rules (cron jobs, gates). Trailing \`*\` is a prefix wildcard. Executors (a session with a parentSessionId) are closable regardless. Default ${JSON.stringify(DEFAULT_CLOSABLE_ORIGINS)}.`, items: { type: "string" }, default: DEFAULT_CLOSABLE_ORIGINS },
1037
+ closableOrigins: { type: "array", description: `Origins that may be closed under the current rules (cron jobs, gates, workflow steps, reviewer lanes). Trailing \`*\` is a prefix wildcard. Executors (a session with a parentSessionId) are closable regardless. Default ${JSON.stringify(DEFAULT_CLOSABLE_ORIGINS)}.`, items: { type: "string" }, default: DEFAULT_CLOSABLE_ORIGINS },
1004
1038
  },
1005
1039
  outputs: {},
1006
1040
  steps: [
@@ -1038,7 +1072,10 @@ export default {
1038
1072
  id: "autoApplyOne",
1039
1073
  kind: "tool",
1040
1074
  tool: "session_wrapup_apply",
1041
- inputs: { sessionIds: ["$item.sessionId"], verdict: "$item.verdict", note: "$item.note" },
1075
+ // `wait: true` — past its 25 s default `waitMs` the tool returns a
1076
+ // bare `{ jobId, status: "running" }` and the report would show no
1077
+ // outcome for a session that did get closed.
1078
+ inputs: { sessionIds: ["$item.sessionId"], verdict: "$item.verdict", note: "$item.note", wait: true },
1042
1079
  },
1043
1080
  ],
1044
1081
  },
@@ -1229,7 +1266,8 @@ export default {
1229
1266
  id: "judgedApplyOne",
1230
1267
  kind: "tool",
1231
1268
  tool: "session_wrapup_apply",
1232
- inputs: { sessionIds: ["$item.sessionId"], verdict: "$item.verdict", judgedBy: "$item.judgedBy", note: "$item.note" },
1269
+ // `wait: true`: same as autoApplyOne.
1270
+ inputs: { sessionIds: ["$item.sessionId"], verdict: "$item.verdict", judgedBy: "$item.judgedBy", note: "$item.note", wait: true },
1233
1271
  },
1234
1272
  ],
1235
1273
  },
@@ -12,7 +12,7 @@ export const DEFAULT_USER_ORIGINS = ["chat-starter", "vscode"]
12
12
  /** Origins the steward MAY close under the current rules. `cron:*` matches
13
13
  * every cron-spawned job (`origin: "cron:<jobId>"`); `gate` matches
14
14
  * supervision-gate sessions. */
15
- export const DEFAULT_CLOSABLE_ORIGINS = ["cron:*", "gate"]
15
+ export const DEFAULT_CLOSABLE_ORIGINS = ["cron:*", "gate", "workflow", "review"]
16
16
 
17
17
  /** The reason shown (and recorded) when a would-be close is bounded by origin. */
18
18
  export const USER_ORIGIN_REASON = "flag (origine utilisateur)"
@@ -46,7 +46,7 @@ export function resolveOriginPolicy(policy) {
46
46
  /** The origin class of a candidate:
47
47
  * - `"user"` — human-launched: a `userOrigins` match, OR a root with no
48
48
  * origin and no parent. FLAG ONLY, never close.
49
- * - `"closable"` — a `closableOrigins` match (cron:*, gate) or an executor
49
+ * - `"closable"` — a `closableOrigins` match (cron:*, gate, workflow, review) or an executor
50
50
  * (has a `parentSessionId`). Close allowed under the current rules.
51
51
  * `userOrigins` wins over both `closableOrigins` and the executor rule, so a
52
52
  * `vscode` executor is still user-origin. An unrecognized root origin is
@@ -33,10 +33,18 @@ loader path can carry.
33
33
  judge session is released (killed + archived) when its item settles.
34
34
  Verdicts: `done|abandoned|blocked|needs-input|active`. A judge error never
35
35
  closes anything.
36
- 5. `ask` (only with `askSessions`) — low-confidence, idle, non-keepAlive,
37
- not-awaiting-input sessions get ONE prompt asking them to reply
36
+ 5. `ask` (only with `askSessions`) — low-confidence, idle, not-awaiting-input
37
+ sessions get ONE prompt asking them to reply
38
38
  `STEWARD: DONE …` / `STEWARD: NOT-DONE …`; a ~3 min bounded wait; the
39
- answer becomes a `declared` verdict.
39
+ answer becomes a `declared` verdict. `keepAlive` only re-lights a session
40
+ after a daemon restart, so it does not exempt one from the ask: a keepAlive
41
+ session whose worktree is merged or clean (nothing uncommitted, nothing
42
+ ahead of base; no worktree info counts as not clean) is asked too. Closing
43
+ is an active act: an on-demand run (`agentproto steward --ask-sessions`)
44
+ adds no idle delay beyond `--idle`. Only a scheduled run (`recurring: true`,
45
+ as the hourly routine sets) waits `keepAliveAskAfterMinutes` (default 1440,
46
+ 24 h; 0 turns it off), so a session you meant to resume is not closed
47
+ overnight.
40
48
  6. `judgedApply` (only with `apply`) — confident `done`/`abandoned` close
41
49
  (resumable, with a recorded outcome); confident `blocked`/`needs-input`
42
50
  only flag. Everything else is left alone and reported.
@@ -53,7 +61,7 @@ the `userOrigins` / `closableOrigins` inputs:
53
61
  `origin` and no `parentSessionId` (a human launched it). A would-be close —
54
62
  even a rule-certain `close`/`stuck`, even a confident `done` — is recorded
55
63
  as a `needs-input` flag with reason `flag (origine utilisateur)`.
56
- - **Close allowed:** `cron:*`, `gate`, and executors (a session with a
64
+ - **Close allowed:** `cron:*`, `gate`, `workflow` (step sessions), `review` (reviewer lanes), and executors (a session with a
57
65
  `parentSessionId`).
58
66
 
59
67
  A trailing `*` in either list is a prefix wildcard. The report carries the
@@ -21,12 +21,15 @@ target:
21
21
  inputs:
22
22
  apply: true
23
23
  askSessions: false
24
+ # Scheduled run: if askSessions is ever turned on here, a keepAlive session
25
+ # is asked only after keepAliveAskAfterMinutes idle (default 1440, 0 = never).
26
+ recurring: true
24
27
  # Origin policy (the committed default): never close a human's session.
25
28
  # `chat-starter`/`vscode` (and any root with no origin and no parent) are
26
- # FLAG-ONLY; `cron:*` jobs, `gate` sessions, and executors (a session with
29
+ # FLAG-ONLY; `cron:*` jobs, `gate`, `workflow` and `review` sessions, and executors (a session with
27
30
  # a parentSessionId) stay closeable. A trailing `*` is a prefix wildcard.
28
31
  userOrigins: ["chat-starter", "vscode"]
29
- closableOrigins: ["cron:*", "gate"]
32
+ closableOrigins: ["cron:*", "gate", "workflow", "review"]
30
33
  retry:
31
34
  max_attempts: 1
32
35
  backoff: fixed
@@ -68,7 +71,7 @@ The steward bounds every action by the candidate's `origin` (pure
68
71
  `origin` and no `parentSessionId` (a human launched it). A would-be close —
69
72
  even a rule-certain `close`/`stuck`, even a confident `done` — becomes a
70
73
  `needs-input` flag with reason `flag (origine utilisateur)`.
71
- - **Close allowed:** `cron:*` (any cron job), `gate`, and executors (a
74
+ - **Close allowed:** `cron:*` (any cron job), `gate`, `workflow` (workflow-step sessions), `review` (reviewer lanes), and executors (a
72
75
  session with a `parentSessionId`).
73
76
 
74
77
  Both lists are workflow inputs (`userOrigins`, `closableOrigins`); the values