@zq-silk/yui 2.16.0 → 2.18.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 (118) hide show
  1. package/dist/cli/commandCatalog.js +19 -12
  2. package/dist/cli/interactionCandidates.js +20 -5
  3. package/dist/cli/interactionPolicy.js +5 -4
  4. package/dist/cli/roleWizard.js +7 -0
  5. package/dist/commands/controllerCommands.js +13 -4
  6. package/dist/commands/durableJobCommands.js +50 -3
  7. package/dist/commands/globalRoleCommands.js +31 -14
  8. package/dist/commands/inboxCommands.js +1 -0
  9. package/dist/commands/operatorCommands.js +12 -2
  10. package/dist/commands/profileCommands.js +19 -2
  11. package/dist/commands/roleConfiguration.js +3 -0
  12. package/dist/commands/taskCommands.js +16 -0
  13. package/dist/commands/taskRoleRuntimeStatus.js +3 -0
  14. package/dist/controlPlaneCli.js +21 -4
  15. package/dist/controller/agentJobControl.js +9 -1
  16. package/dist/controller/clientRuntime.js +34 -13
  17. package/dist/controller/controller.js +35 -22
  18. package/dist/controller/diagnosticJournal.js +214 -0
  19. package/dist/controller/fileSchedulerStoreAdapter.js +174 -105
  20. package/dist/controller/globalInputDelivery.js +19 -9
  21. package/dist/controller/globalRuntimeAttention.js +11 -3
  22. package/dist/controller/jobControl.js +1 -1
  23. package/dist/controller/jobDefinitionControl.js +24 -13
  24. package/dist/controller/jobSupervisor.js +4 -0
  25. package/dist/controller/providerRetryAdmission.js +9 -0
  26. package/dist/controller/providerRetryDelivery.js +16 -0
  27. package/dist/controller/runtime.js +80 -4
  28. package/dist/controller/runtimeLaunchCoordinator.js +3 -1
  29. package/dist/controller/workerSchedulerStore.js +24 -0
  30. package/dist/core/boundedRpc.js +4 -0
  31. package/dist/core/controllerServer.js +1 -1
  32. package/dist/core/controllerTelemetry.js +92 -19
  33. package/dist/executor/fileRoleLaunchPlanner.js +3 -0
  34. package/dist/job/globalShell.js +51 -0
  35. package/dist/job/jobBusinessResult.js +4 -3
  36. package/dist/job/jobDefinition.js +6 -1
  37. package/dist/job/jobRunner.js +9 -0
  38. package/dist/job/scheduleService.js +2 -0
  39. package/dist/message/globalInboxRecovery.js +30 -19
  40. package/dist/message/globalProviderRetry.js +7 -1
  41. package/dist/message/messageContinuation.js +4 -0
  42. package/dist/output/rolePresentation.js +3 -0
  43. package/dist/profile/agentProfile.js +8 -0
  44. package/dist/profile/agentProfileRuntime.js +16 -1
  45. package/dist/review/reviewDecision.js +6 -0
  46. package/dist/role/availability.js +28 -0
  47. package/dist/role/role.js +6 -0
  48. package/dist/runtime/builtinAgentErrorMappers.js +4 -0
  49. package/dist/runtime/codexNativeAccess.js +21 -4
  50. package/dist/runtime/globalSessionHealth.js +39 -0
  51. package/dist/runtime/tmuxAdapters.js +5 -0
  52. package/dist/scheduler/activeRoleRunDelivery.js +9 -0
  53. package/dist/scheduler/leaderWakeupProcessor.js +8 -1
  54. package/dist/scheduler/roleRunLiveness.js +3 -0
  55. package/dist/scheduler/roleRunStall.js +10 -10
  56. package/dist/storage/persistenceWorker.js +41 -19
  57. package/dist/storage/sqliteSchema.js +43 -9
  58. package/dist/storage/sqliteStore.js +86 -8
  59. package/dist/storage/storageVersions.js +1 -1
  60. package/dist/storage/storeRpc.js +25 -11
  61. package/dist/storage/taskReadRevisionSchema.js +39 -0
  62. package/dist/web/assets/client/app/catalog.js +0 -29
  63. package/dist/web/assets/client/app/jobs.js +115 -0
  64. package/dist/web/assets/client/app/main.js +32 -28
  65. package/dist/web/assets/client/app/manage.js +1 -0
  66. package/dist/web/assets/client/app/settings.js +1 -5
  67. package/dist/web/assets/client/app/taskView.js +7 -11
  68. package/dist/web/assets/client/i18n/en.js +265 -36
  69. package/dist/web/assets/client/i18n/zh-CN.js +264 -35
  70. package/dist/web/assets/client/index.js +4 -0
  71. package/dist/web/assets/client/layout/manageShell.js +61 -15
  72. package/dist/web/assets/client/layout/router.js +2 -1
  73. package/dist/web/assets/client/layout/shortcuts.js +2 -2
  74. package/dist/web/assets/client/layout/workspace.js +99 -112
  75. package/dist/web/assets/client/lib/prefs.js +1 -1
  76. package/dist/web/assets/client/ui/primitives.js +15 -2
  77. package/dist/web/assets/client/views/dock/conversation.js +421 -123
  78. package/dist/web/assets/client/views/dock/session.js +229 -0
  79. package/dist/web/assets/client/views/dock/terminal.js +48 -39
  80. package/dist/web/assets/client/views/jobs.js +1042 -160
  81. package/dist/web/assets/client/views/overview.js +2 -2
  82. package/dist/web/assets/client/views/sidebar.js +3 -0
  83. package/dist/web/assets/client/views/task/page.js +2 -12
  84. package/dist/web/assets/shared/manageNav.js +3 -1
  85. package/dist/web/assets/shell/appRail.js +2 -1
  86. package/dist/web/assets/shell/dock.js +9 -27
  87. package/dist/web/assets/shell/manage.js +7 -2
  88. package/dist/web/assets/shell/models.js +4 -3
  89. package/dist/web/assets/shell/settingsContent.js +0 -3
  90. package/dist/web/assets/shell/sidebar.js +0 -1
  91. package/dist/web/assets/styles/views/conversation.js +96 -17
  92. package/dist/web/assets/styles/views/dock.js +17 -5
  93. package/dist/web/assets/styles/views/jobs.js +301 -12
  94. package/dist/web/assets/styles/views/sidebar.js +1 -2
  95. package/dist/web/assets/styles/views/task.js +1 -2
  96. package/dist/web/assets/styles/views/terminal.js +12 -8
  97. package/dist/web/webConversation.js +1 -0
  98. package/dist/web/webServer.js +28 -1
  99. package/dist/web/webSettings.js +10 -2
  100. package/docs/global-shell-jobs.md +78 -0
  101. package/docs/job-runs.md +6 -3
  102. package/docs/managed-turn-and-session-runtime.md +84 -0
  103. package/docs/managed-turn-and-session-runtime.zh-CN.md +57 -0
  104. package/docs/release-workflow.md +67 -0
  105. package/docs/release-workflow.zh-CN.md +48 -0
  106. package/docs/roles-and-configuration.md +45 -1
  107. package/docs/roles-and-configuration.zh-CN.md +35 -0
  108. package/docs/storage-baseline.md +19 -1
  109. package/docs/storage-baseline.zh-CN.md +10 -1
  110. package/docs/testing/review-evaluation.md +35 -1
  111. package/docs/testing/review-evaluation.zh-CN.md +25 -1
  112. package/docs/testing/verification-levels.md +2 -2
  113. package/docs/testing/verification-levels.zh-CN.md +2 -2
  114. package/package.json +1 -1
  115. package/skills/yui-leader/VERSION +1 -1
  116. package/skills/yui-leader/references/execution.md +19 -8
  117. package/skills/yui-reviewer/SKILL.md +13 -2
  118. package/skills/yui-reviewer/VERSION +1 -1
@@ -1,19 +1,308 @@
1
+ // Jobs: one calm, dense surface. Hairline separators instead of nested cards,
2
+ // the workbench's own list head and "new" action, a two-line index, a scannable Run table and a properties
3
+ // column. Only shared tokens (ink, line, surface, tone, radius, type scale).
1
4
  export const JOBS_STYLES = `
2
- .jobs-manager { min-width: 0; display: grid; gap: 16px; container-type: inline-size; }
3
- .jobs-layout { display: grid; grid-template-columns: minmax(160px, 240px) minmax(0, 1fr); gap: 24px; align-items: start; }
4
- .jobs-list, .jobs-detail, .jobs-runs { display: grid; gap: 12px; min-width: 0; }
5
- .job-choice { display: grid; gap: 6px; text-align: left; padding: 14px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); color: inherit; cursor: pointer; overflow-wrap: anywhere; }
6
- .job-choice[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); }
7
- .job-choice:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
8
- .jobs-manager .settings-actions { flex-wrap: wrap; }
9
- .jobs-manager .settings-observation { max-width: 100%; max-height: 420px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }
10
- .jobs-manager .btn, .job-fact { overflow-wrap: anywhere; white-space: normal; text-align: left; }
11
- .jobs-runs .btn { font-family: var(--font-mono); font-size: 12px; }
12
- .jobs-manager textarea { width: 100%; font-family: var(--font-mono); }
5
+ .jobs-manager { min-width: 0; display: grid; gap: 14px; container-type: inline-size; }
13
6
  .jobs-manager [hidden] { display: none !important; }
7
+ .jobs-manager textarea { width: 100%; font-family: var(--font-mono); font-size: var(--fs-xs); }
8
+ .jobs-manager .settings-observation { max-width: 100%; max-height: 420px; margin: 0; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--fs-xs); }
9
+ .jobs-manager .btn { height: 30px; padding: 0 11px; }
10
+ .jobs-layout { display: grid; grid-template-columns: minmax(180px, 240px) minmax(0, 1fr); gap: 20px; align-items: start; }
11
+ .jobs-list, .jobs-detail { display: grid; gap: 12px; min-width: 0; }
12
+
13
+ /* Page bar */
14
+ .jobs-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; min-width: 0; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
15
+ .jobs-bar-title { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
16
+ .jobs-bar-title h1, .jobs-bar-title h2 { font-size: var(--fs-lg); font-weight: 650; letter-spacing: -.01em; }
17
+ .jobs-bar-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px; min-width: 0; }
18
+ .jobs-bar-actions .settings-receipt { margin: 0 6px 0 0; color: var(--ink-4); font-size: var(--fs-xs); white-space: pre-line; }
19
+ .jobs-bar-actions .settings-receipt:empty { display: none; }
20
+ .jobs-bar-actions .settings-receipt.settings-error { order: 9; flex-basis: 100%; margin: 4px 0 0; padding: 8px 10px; border-radius: var(--r-sm); background: var(--bad-soft); color: var(--bad); text-align: left; }
21
+ .jobs-receipt .settings-receipt { margin: 0; color: var(--ink-3); font-size: var(--fs-xs); white-space: pre-line; }
22
+ .jobs-receipt .settings-receipt:empty, .jobs-receipt:has(.settings-receipt:empty) { display: none; }
23
+ .jobs-receipt .settings-receipt.settings-error { padding: 8px 10px; border-radius: var(--r-sm); background: var(--bad-soft); color: var(--bad); }
24
+ .job-notice { margin: 0; padding: 8px 12px; border-radius: var(--r-sm); background: var(--surface-3); color: var(--ink-2); font-size: var(--fs-sm); }
25
+
26
+ /* Job index (left rail): two lines per Job, status at the right edge. */
27
+ .jobs-area .jobs-index { display: grid; gap: 10px; min-width: 0; padding-top: 2px; }
28
+ .jobs-area .side-create { margin-bottom: 2px; }
29
+ /* The area title stays whole beside the Jobs sync; a long clock gives way. */
30
+ .manage-rail .side-head:has(#jobs-sync:not([hidden])) .side-title { flex: none; }
31
+ #jobs-sync { min-width: 0; overflow: hidden; white-space: nowrap; }
32
+ #jobs-sync time { overflow: hidden; text-overflow: ellipsis; }
33
+ .jobs-scope { display: grid; gap: 4px; padding: 0 2px; }
34
+ .jobs-scope label { color: var(--ink-4); font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: .07em; }
35
+ .jobs-scope select { width: 100%; min-width: 0; height: 32px; padding-top: 4px; padding-bottom: 4px; font-size: var(--fs-sm); }
36
+ .jobs-scope-help { margin: 0; color: var(--ink-4); font-size: var(--fs-2xs); line-height: 1.4; }
37
+ .jobs-scope-help.is-error, .jobs-index-empty.is-error { color: var(--bad); }
38
+ .jobs-index-body { display: grid; gap: 8px; min-width: 0; }
39
+ .jobs-index-tools { display: grid; gap: 6px; }
40
+ .jobs-index-tools .search-field input { width: 100%; height: 32px; font-size: var(--fs-sm); }
41
+ .jobs-index-count { margin: 0; padding: 0 2px; color: var(--ink-4); font-size: var(--fs-2xs); font-variant-numeric: tabular-nums; }
42
+ .jobs-index-empty { margin: 0; padding: 24px 12px; color: var(--ink-4); font-size: var(--fs-sm); text-align: center; }
43
+ .jobs-list { gap: 1px; }
44
+ .job-row { position: relative; display: flex; align-items: flex-start; gap: 9px; width: 100%; min-width: 0; padding: 7px 8px; border: 0; border-radius: var(--r-sm); background: transparent; color: inherit; text-align: left; cursor: pointer; transition: background var(--t-fast); }
45
+ .job-row > .dot { margin-top: 6px; }
46
+ .job-row:hover { background: var(--surface-3); }
47
+ .job-row[aria-current="true"] { background: var(--surface-3); }
48
+ .job-row[aria-current="true"] .job-row-name { font-weight: 600; }
49
+ .job-row-body { display: grid; gap: 1px; flex: 1; min-width: 0; }
50
+ .job-row-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
51
+ .job-row-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); font-size: var(--fs-sm); font-weight: 500; line-height: 1.45; }
52
+ .job-row-line > .badge { flex: none; height: 18px; padding: 0 6px; font-size: var(--fs-2xs); }
53
+ .job-row-sub { color: var(--ink-4); font-size: var(--fs-2xs); }
54
+ .job-row-sub > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
55
+ .job-row-type { flex: none; max-width: 50%; color: var(--ink-3); }
56
+ .job-row-plan { flex: 1; }
57
+ .job-row-plan::before { content: "·"; margin-right: 6px; color: var(--ink-4); }
58
+ .job-row-none { flex: none; color: var(--ink-4); font-size: var(--fs-2xs); }
59
+
60
+ /* Focus is always visible. */
61
+ .job-row:focus-visible, .job-run-row:focus-visible, .job-copy:focus-visible, .job-more:focus-visible,
62
+ .job-section-action:focus-visible, .job-raw > summary:focus-visible, .job-log > summary:focus-visible, .job-fold > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; box-shadow: none; }
63
+
64
+ /* Job header */
65
+ .job-page { display: grid; gap: 16px; min-width: 0; }
66
+ .job-header { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 10px 16px; min-width: 0; }
67
+ .job-title { display: grid; gap: 6px; flex: 1 1 360px; min-width: 0; }
68
+ .job-title h2 { font-size: var(--fs-xl); font-weight: 650; letter-spacing: -.015em; line-height: 1.25; overflow-wrap: anywhere; }
69
+ .job-badges { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
70
+ .job-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 0; color: var(--ink-3); font-size: var(--fs-xs); }
71
+ .job-meta-item { display: inline-flex; align-items: center; gap: 4px; min-width: 0; }
72
+ .job-meta-item + .job-meta-item::before { content: "·"; margin: 0 8px; color: var(--ink-4); }
73
+ .job-meta a { color: inherit; }
74
+ .job-actions { display: flex; flex-wrap: wrap; gap: 6px; flex: none; }
75
+ .job-note { margin: 0; color: var(--ink-4); font-size: var(--fs-xs); line-height: 1.5; }
76
+ .job-note.is-error { color: var(--bad); }
77
+
78
+ /* Overview strip: numbers first, the last 30 Runs as a status strip. */
79
+ .job-overview { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 24px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
80
+ .job-stats { display: flex; flex-wrap: wrap; gap: 10px 0; min-width: 0; }
81
+ .job-stat { display: grid; align-content: start; gap: 2px; min-width: 0; padding: 0 18px; border-left: 1px solid var(--line); }
82
+ .job-stat:first-child { padding-left: 0; border-left: 0; }
83
+ .job-stat-label { color: var(--ink-4); font-size: var(--fs-2xs); white-space: nowrap; }
84
+ .job-stat-value { color: var(--ink); font-size: var(--fs-lg); font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.3; }
85
+ .job-stat-sub { color: var(--ink-4); font-size: var(--fs-2xs); font-variant-numeric: tabular-nums; white-space: nowrap; }
86
+ .job-stat-run { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; min-height: 26px; }
87
+ .job-stat-run code { font-size: var(--fs-xs); color: var(--ink-2); }
88
+ .job-stat-run .badge { height: 20px; padding: 0 7px; font-size: var(--fs-2xs); }
89
+ .job-spark { display: flex; align-items: stretch; gap: 2px; height: 26px; }
90
+ .job-spark-bar { width: 6px; border-radius: 2px; background: var(--tone, var(--line-3)); opacity: .9; }
91
+ .job-spark-bar.tone-idle { background: var(--line-3); }
92
+
93
+ /* About the Job: description and configuration | plans and definition */
94
+ .job-purpose { display: flex; align-items: flex-start; gap: 4px; min-width: 0; padding: 10px 6px 10px 12px; border-left: 2px solid var(--line-3); border-radius: 0 var(--r-sm) var(--r-sm) 0; background: var(--surface-3); }
95
+ .job-purpose .job-prose { flex: 1; max-height: 180px; line-height: 1.6; }
96
+ .job-purpose.is-note { display: block; margin: 0; padding: 8px 12px; color: var(--ink-2); font-size: var(--fs-sm); }
97
+ .job-about { display: grid; grid-template-columns: minmax(0, 1fr) minmax(240px, 300px); gap: 24px; align-items: start; min-width: 0; }
98
+ .job-primary, .job-aside { display: grid; gap: 22px; min-width: 0; }
99
+ .job-section { display: grid; gap: 8px; min-width: 0; }
100
+ .job-section-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 26px; }
101
+ .job-section-head h4 { color: var(--ink-2); font-size: var(--fs-sm); font-weight: 600; }
102
+ .job-section-count { color: var(--ink-4); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
103
+ .job-section-title { margin: 6px 0 0; color: var(--ink-3); font-size: var(--fs-xs); font-weight: 600; }
104
+ .job-section-action { display: inline-flex; align-items: center; gap: 4px; height: 26px; padding: 0 8px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--ink-3); font-size: var(--fs-xs); font-weight: 500; }
105
+ .job-section-action:hover { background: var(--surface-3); color: var(--ink); }
106
+ .job-section-action .icon { width: 14px; height: 14px; }
107
+ .job-empty-block { margin: 0; padding: 14px 12px; border: 1px dashed var(--line-2); border-radius: var(--r-md); color: var(--ink-4); font-size: var(--fs-sm); text-align: center; }
108
+ .job-blank { display: grid; justify-items: center; gap: 10px; padding: 64px 16px; color: var(--ink-4); text-align: center; }
109
+
110
+ /* Run history: a table on wide containers, two lines on narrow ones. */
111
+ .job-history { display: grid; gap: 8px; min-width: 0; }
112
+ .job-filters { gap: 4px; }
113
+ .job-filters .chip-tab { height: 26px; padding: 0 9px; }
114
+ .job-filters .count { color: inherit; opacity: .7; font-variant-numeric: tabular-nums; }
115
+ .job-history-body { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: var(--surface); }
116
+ .job-runs-head, .job-run-row { display: grid; grid-template-columns: 104px minmax(0, 1fr) 112px 72px 16px; align-items: center; gap: 12px; }
117
+ .job-runs-head { padding: 6px 12px; border-bottom: 1px solid var(--line); color: var(--ink-4); font-size: var(--fs-2xs); font-weight: 500; }
118
+ .job-runs-head > :nth-child(n+3), .job-run-when, .job-run-span { text-align: right; }
119
+ .jobs-runs { display: grid; }
120
+ .job-run-row { scroll-margin-top: calc(var(--header-h, 56px) + 12px); width: 100%; min-width: 0; padding: 7px 12px; border: 0; border-top: 1px solid var(--line); background: transparent; color: inherit; text-align: left; cursor: pointer; transition: background var(--t-fast); }
121
+ .jobs-runs > .job-run-row:first-child { border-top: 0; }
122
+ .job-run-row:hover { background: var(--surface-3); }
123
+ .job-run-row[aria-current="true"] { background: var(--surface-3); box-shadow: inset 2px 0 0 var(--accent); }
124
+ .job-run-caret { display: inline-grid; place-items: center; color: var(--ink-4); }
125
+ .job-run-caret .icon { width: 14px; height: 14px; transition: transform var(--t-fast); }
126
+ .job-run-row[aria-expanded="true"] .job-run-caret .icon { transform: rotate(90deg); color: var(--ink-2); }
127
+ .job-run-status .badge { height: 20px; padding: 0 7px; font-size: var(--fs-2xs); }
128
+ .job-run-main { display: grid; gap: 1px; min-width: 0; }
129
+ .job-run-summary { overflow: hidden; color: var(--ink); font-size: var(--fs-sm); text-overflow: ellipsis; white-space: nowrap; }
130
+ .job-run-sub { display: flex; gap: 8px; min-width: 0; color: var(--ink-4); font-size: var(--fs-2xs); white-space: nowrap; }
131
+ .job-run-sub code { color: var(--ink-3); }
132
+ .job-run-when, .job-run-span { color: var(--ink-3); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }
133
+ .job-more { width: 100%; padding: 8px 12px; border: 0; border-top: 1px solid var(--line); background: transparent; color: var(--ink-3); font-size: var(--fs-xs); font-weight: 500; }
134
+ .job-more:hover { background: var(--surface-3); color: var(--ink); }
135
+
136
+ /* Selected Run: opened in place, directly under its row */
137
+ .job-run-inline { padding: 4px 12px 14px; border-top: 1px dashed var(--line); background: var(--surface-3); box-shadow: inset 2px 0 0 var(--accent); }
138
+ .jobs-runs > .job-run-inline:first-child { border-top: 0; }
139
+ .job-run-inline .job-run-panel { border-color: var(--line); box-shadow: var(--shadow-1); }
140
+ .job-run-inline:empty, .job-run-panel > section:empty, .job-run-artifacts > div:empty { display: none; }
141
+ .job-run-panel > .job-kv { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 24px; }
142
+ .job-run-panel > .job-kv > .job-kv-row:nth-child(2) { border-top: 0; }
143
+ .job-run-panel { display: grid; gap: 12px; min-width: 0; padding: 14px; border: 1px solid var(--line-2); border-radius: var(--r-md); background: var(--surface); }
144
+ .job-run-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
145
+ .job-run-head > .job-badges { flex: none; flex-wrap: nowrap; }
146
+ .job-run-title { display: flex; flex: 1; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; min-height: 28px; }
147
+ .job-run-title h4 { font-size: var(--fs-sm); font-weight: 600; }
148
+ .job-run-close { width: 28px; height: 28px; }
149
+ .job-result { margin: 0; padding: 8px 10px; border-left: 2px solid var(--tone, var(--line-3)); border-radius: 0 var(--r-sm) var(--r-sm) 0; background: var(--tone-soft, var(--surface-3)); color: var(--ink); font-size: var(--fs-sm); overflow-wrap: anywhere; }
150
+ .job-result-label { margin-right: 8px; color: var(--ink-3); font-size: var(--fs-xs); font-weight: 600; }
151
+ .job-run-steps { display: grid; margin: 0; padding: 0; list-style: none; border: 1px solid var(--line); border-radius: var(--r-sm); }
152
+ .job-run-step { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; padding: 6px 10px; border-top: 1px solid var(--line); font-size: var(--fs-xs); }
153
+ .job-run-step:first-child { border-top: 0; }
154
+ .job-run-step .badge { height: 20px; font-size: var(--fs-2xs); }
155
+ .job-run-step-meta { margin-left: auto; color: var(--ink-4); font-variant-numeric: tabular-nums; }
156
+ .job-logs { display: grid; gap: 6px; }
157
+ .job-log { border: 1px solid var(--line); border-radius: var(--r-sm); }
158
+ .job-log > summary { display: flex; align-items: center; gap: 8px; padding: 6px 10px; font-size: var(--fs-xs); cursor: pointer; }
159
+ .job-log-text { max-height: 320px; margin: 0; padding: 10px; overflow: auto; border-top: 1px solid var(--line); background: var(--sunken); font-size: var(--fs-2xs); line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
160
+ .job-artifact-list { display: flex; flex-wrap: wrap; gap: 6px; }
161
+ .job-inline-action { justify-self: start; height: 26px !important; padding: 0 8px !important; color: var(--ink-3); font-size: var(--fs-xs); }
162
+ .job-run-evidence, .job-run-artifacts { display: grid; gap: 6px; min-width: 0; }
163
+ .job-advanced { display: grid; gap: 6px; }
164
+
165
+ /* Label/value lists (configuration, definition, Run facts) */
166
+ .job-kv { display: grid; margin: 0; min-width: 0; }
167
+ .job-kv-row { display: grid; grid-template-columns: 132px minmax(0, 1fr); gap: 12px; padding: 6px 0; border-top: 1px solid var(--line); font-size: var(--fs-sm); }
168
+ .job-kv-row:first-child { border-top: 0; }
169
+ .job-kv dt { color: var(--ink-4); font-size: var(--fs-xs); line-height: 1.7; overflow-wrap: anywhere; }
170
+ .job-kv dd { margin: 0; min-width: 0; color: var(--ink); overflow-wrap: anywhere; }
171
+ .job-kv.is-compact .job-kv-row { grid-template-columns: 84px minmax(0, 1fr); padding: 5px 0; }
172
+ .job-kv.is-nested { padding-left: 10px; border-left: 2px solid var(--line); }
173
+ .job-kv.is-nested .job-kv-row { grid-template-columns: minmax(80px, 120px) minmax(0, 1fr); padding: 3px 0; border-top: 0; }
174
+ .job-key { color: var(--ink-3); font-size: var(--fs-xs); }
175
+ .job-empty { color: var(--ink-4); }
176
+ .job-muted { color: var(--ink-4); font-size: var(--fs-xs); }
177
+ .job-text { white-space: pre-wrap; }
178
+ .job-kind { font-weight: 600; }
179
+ .job-prose { max-height: 240px; margin: 0; overflow: auto; color: var(--ink); font-family: inherit; font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
180
+ .job-chips { display: flex; flex-wrap: wrap; gap: 4px; }
181
+ .job-chip { padding: 1px 6px; border-radius: var(--r-xs); background: var(--surface-3); color: var(--ink-2); font-size: var(--fs-2xs); overflow-wrap: anywhere; }
182
+ .job-chip-key { color: var(--ink); font-weight: 600; }
183
+ .job-list { margin: 0; padding-left: 18px; }
184
+ .job-fold > summary { color: var(--ink-3); font-size: var(--fs-xs); cursor: pointer; }
185
+ .job-time { display: inline-flex; flex-wrap: wrap; gap: 0 8px; font-variant-numeric: tabular-nums; }
186
+ .job-time-ago { color: var(--ink-4); font-size: var(--fs-xs); }
187
+ .job-id, .job-path { display: inline-flex; align-items: center; gap: 2px; max-width: 100%; min-width: 0; }
188
+ .job-path code { min-width: 0; overflow-wrap: anywhere; font-size: var(--fs-xs); }
189
+ .job-copy { display: inline-grid; place-items: center; width: 22px; height: 22px; flex: none; padding: 0; border: 0; border-radius: var(--r-xs); background: transparent; color: var(--ink-4); }
190
+ .job-copy:hover { background: var(--surface-3); color: var(--ink); }
191
+ .job-copy .icon { width: 13px; height: 13px; }
192
+ .job-copy.is-copied { color: var(--ok); }
193
+ .job-copy-text { display: inline-flex; align-items: center; width: auto; gap: 4px; padding: 0 6px; font-size: var(--fs-2xs); white-space: nowrap; }
194
+
195
+ /* Configuration: steps as numbered rows with their command */
196
+ .job-config { display: grid; gap: 14px; min-width: 0; }
197
+ .job-steps { display: grid; margin: 0; padding: 0; list-style: none; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
198
+ .job-step { display: grid; gap: 6px; padding: 10px 12px; border-top: 1px solid var(--line); min-width: 0; }
199
+ .job-step:first-child { border-top: 0; }
200
+ .job-step-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }
201
+ .job-step-index { display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: var(--r-pill); background: var(--surface-3); color: var(--ink-3); font-size: var(--fs-2xs); font-weight: 600; font-variant-numeric: tabular-nums; }
202
+ .job-step-name { color: var(--ink); font-size: var(--fs-xs); font-weight: 600; }
203
+ .job-tag { max-width: 100%; padding: 0 6px; border: 1px solid var(--line-2); border-radius: var(--r-xs); color: var(--ink-3); font-size: var(--fs-2xs); line-height: 18px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
204
+ .job-command { display: flex; align-items: flex-start; gap: 4px; min-width: 0; padding: 6px 6px 6px 10px; border-radius: var(--r-sm); background: var(--sunken); }
205
+ .job-command pre { flex: 1; min-width: 0; margin: 0; padding-top: 2px; color: var(--ink); font-size: var(--fs-xs); line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
206
+ .job-command.is-prose pre { font-family: inherit; font-size: var(--fs-sm); }
207
+ .job-step-env { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
208
+ .job-step-label { color: var(--ink-4); font-size: var(--fs-2xs); }
209
+
210
+ /* Exact JSON, one disclosure away */
211
+ .job-raw { position: relative; min-width: 0; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); overflow: hidden; }
212
+ .job-raw > summary { display: flex; align-items: center; gap: 8px; min-height: 34px; padding: 7px 110px 7px 12px; color: var(--ink-3); font-size: var(--fs-xs); cursor: pointer; list-style: none; }
213
+ .job-raw > summary::before { content: "›"; font-size: 16px; line-height: 1; transform-origin: center; }
214
+ .job-raw[open] > summary::before { transform: rotate(90deg); }
215
+ .job-raw > summary:hover { color: var(--ink); }
216
+ .job-raw-hint { padding: 0 5px; border: 1px solid var(--line-2); border-radius: var(--r-xs); color: var(--ink-4); font-family: var(--font-mono); font-size: 10px; line-height: 16px; }
217
+ .job-raw-body { min-width: 0; border-top: 1px solid var(--line); }
218
+ .job-raw-tools { position: absolute; right: 8px; top: 4px; display: flex; }
219
+ .job-raw .settings-observation { display: block; margin: 0; padding: 14px; max-height: 320px; overflow: auto; background: var(--sunken); color: var(--ink-2); font: var(--fs-xs)/1.65 var(--font-mono); white-space: pre; tab-size: 2; }
220
+ .job-form-advanced { min-width: 0; color: var(--ink-3); font-size: var(--fs-xs); }
221
+ .job-form-advanced > summary { cursor: pointer; padding: 8px 0; }
222
+ .job-form-advanced[open] > :not(summary) { margin-top: 8px; }
223
+ .job-form-advanced .job-raw { margin-top: 10px; }
224
+ .job-form-advanced:has(:focus-visible), .job-raw > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
225
+
226
+ /* Plans */
227
+ .job-plans { display: grid; margin: 0; padding: 0; list-style: none; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
228
+ .job-plan { display: grid; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--line); min-width: 0; }
229
+ .job-plan:first-child { border-top: 0; }
230
+ .job-plan-main { display: grid; gap: 4px; min-width: 0; }
231
+ .job-plan-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; min-width: 0; }
232
+ .job-plan-line .badge { height: 20px; padding: 0 7px; font-size: var(--fs-2xs); }
233
+ .job-plan-rule { font-size: var(--fs-sm); font-weight: 600; }
234
+ .job-plan-exact { color: var(--ink-4); font-family: var(--font-mono); font-size: var(--fs-2xs); overflow-wrap: anywhere; }
235
+ .job-plan-sub { color: var(--ink-4); font-size: var(--fs-2xs); }
236
+ .job-plan-sub code { color: var(--ink-3); overflow-wrap: anywhere; }
237
+ .job-plan-actions { display: flex; flex-wrap: wrap; gap: 4px; }
238
+ .job-plan-actions .btn { height: 26px; padding: 0 8px; font-size: var(--fs-xs); }
239
+
240
+ /* Editor (a modal dialog, as the workbench's new Task) */
241
+ .job-dialog { width: min(760px, calc(100vw - 32px)); }
242
+ .job-dialog .job-form { gap: 14px; }
243
+ .job-dialog .dialog-actions { position: sticky; bottom: -20px; margin: 0 -20px -20px; padding: 12px 20px; border-top: 1px solid var(--line); background: var(--surface); }
244
+ .job-dialog-receipt { margin: 0; color: var(--ink-3); font-size: var(--fs-xs); white-space: pre-line; }
245
+ .job-dialog-receipt:empty { display: none; }
246
+ .job-dialog-receipt.settings-error { padding: 8px 10px; border-radius: var(--r-sm); background: var(--bad-soft); color: var(--bad); }
247
+ .job-fieldset { display: grid; gap: 10px; min-width: 0; margin: 0; padding: 12px 0 0; border: 0; border-top: 1px solid var(--line); }
248
+ .job-fieldset > legend { padding: 0 8px 0 0; color: var(--ink-2); font-size: var(--fs-sm); font-weight: 600; }
249
+ .job-field-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px 12px; }
250
+ .job-field-stack { display: grid; gap: 10px; }
251
+ .job-field-stack[hidden], .job-form [hidden] { display: none; }
252
+ .job-bound-target { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; padding: 10px 12px; border-radius: var(--r-sm); background: var(--sunken); color: var(--ink-3); font-size: var(--fs-sm); }
253
+ .job-bound-target strong { color: var(--ink); font-weight: 500; overflow-wrap: anywhere; }
254
+ .job-weekdays { display: flex; flex-wrap: wrap; gap: 6px; }
255
+ .job-weekdays .btn { min-width: 38px; height: 34px; padding: 0 10px; }
256
+ .job-weekdays .btn[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--surface); }
257
+ .job-rule-preview { padding: 10px 12px; margin: 0; border-left: 2px solid var(--accent); background: var(--sunken); color: var(--ink-2); font-size: var(--fs-sm); overflow-wrap: anywhere; }
258
+ .job-rule-preview { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; border-radius: var(--r-sm); }
259
+ .job-rule-preview.is-error { border-color: var(--bad); color: var(--bad); background: var(--bad-soft); }
260
+ .job-plan-expression { color: var(--ink-3); font-size: var(--fs-xs); overflow-wrap: anywhere; }
261
+ .job-clock input { font-variant-numeric: tabular-nums; }
262
+ .job-time-shortcuts { display: flex; flex-wrap: wrap; gap: 6px; }
263
+ .job-time-shortcuts .btn { height: 28px; padding: 0 10px; color: var(--ink-3); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
264
+ .job-cron-guide { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); margin: 0; padding: 12px; gap: 8px; border: 1px solid var(--line); border-radius: var(--r-sm); }
265
+ .job-cron-guide dt { color: var(--ink-3); font-size: var(--fs-xs); }
266
+ .job-cron-guide dd { display: grid; gap: 6px; margin: 6px 0 0; overflow-wrap: anywhere; }
267
+ .job-cron-guide dd span { color: var(--ink-4); font-size: var(--fs-2xs); }
268
+ @media (max-width: 480px) {
269
+ .job-cron-guide { grid-template-columns: repeat(3, minmax(0, 1fr)); }
270
+ }
271
+ .job-edit-row { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--line); border-radius: var(--r-sm); min-width: 0; }
272
+ .job-edit-row > .btn, .job-field-stack > .btn { justify-self: start; }
273
+ .job-form textarea { width: 100%; resize: vertical; min-height: 88px; font-size: var(--fs-sm); }
274
+ .job-form input, .job-form select, .job-form textarea { min-width: 0; }
275
+ .jobs-manager form .field > span { color: var(--ink-3); font-size: var(--fs-xs); font-weight: 500; }
276
+ .jobs-manager form input, .jobs-manager form select { height: 32px; padding-top: 4px; padding-bottom: 4px; font-size: var(--fs-sm); }
277
+ .job-config-preview { display: grid; gap: 8px; padding: 12px; border: 1px dashed var(--line-2); border-radius: var(--r-md); }
278
+ .job-preview-label { color: var(--ink-4); font-size: var(--fs-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; }
279
+
14
280
  @media (max-width: 720px) { .jobs-layout { grid-template-columns: minmax(0, 1fr); } }
281
+ @container (max-width: 860px) {
282
+ .job-about { grid-template-columns: minmax(0, 1fr); }
283
+ }
15
284
  @container (max-width: 640px) {
16
285
  .jobs-layout { grid-template-columns: minmax(0, 1fr); }
17
- .jobs-list { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
286
+ .jobs-bar-refresh > span { display: none; }
287
+ .job-actions { flex-wrap: wrap; }
288
+ .job-overview { padding: 10px 12px; }
289
+ .job-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 12px; width: 100%; }
290
+ .job-stat { padding: 0; border-left: 0; }
291
+ .job-spark { width: 100%; }
292
+ .job-spark-bar { flex: 1; width: auto; max-width: 8px; }
293
+ .job-runs-head { display: none; }
294
+ .job-run-row { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "main status" "main when"; gap: 2px 10px; padding: 8px 10px; }
295
+ .job-run-caret { display: none; }
296
+ .job-run-inline { padding: 2px 8px 10px; }
297
+ .job-run-status { grid-area: status; justify-self: end; }
298
+ .job-run-main { grid-area: main; }
299
+ .job-run-when { grid-area: when; }
300
+ .job-run-span { display: none; }
301
+ .job-run-summary { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
302
+ .job-kv-row, .job-kv.is-compact .job-kv-row { grid-template-columns: 92px minmax(0, 1fr); gap: 10px; }
303
+ .job-kv.is-nested .job-kv-row { grid-template-columns: minmax(0, 1fr); }
304
+ .job-run-panel { padding: 12px; }
305
+ .job-run-panel > .job-kv { grid-template-columns: minmax(0, 1fr); }
306
+ .job-run-panel > .job-kv > .job-kv-row:nth-child(2) { border-top: 1px solid var(--line); }
18
307
  }
19
308
  `;
@@ -30,8 +30,6 @@ export const SIDEBAR_STYLES = `
30
30
  .search-field:focus-within kbd{opacity:0}
31
31
  .side-search-body{width:34px;height:34px;border:1px solid var(--line-2);border-radius:var(--r-md);background:var(--surface-2)}
32
32
  .side-search-body:hover:not(:disabled){border-color:var(--line-3)}
33
- .source-seg{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));width:100%}
34
- .source-seg .seg-btn{justify-content:center;min-width:0;overflow:hidden;text-overflow:ellipsis}
35
33
  .attention-bar:empty{display:none}
36
34
  .attention-list{display:flex;flex-wrap:wrap;gap:6px}
37
35
  .attention-pill{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 9px;border:1px solid transparent;border-radius:var(--r-pill);background:var(--tone-soft);color:var(--tone);font-size:var(--fs-xs);font-weight:500}
@@ -63,6 +61,7 @@ export const SIDEBAR_STYLES = `
63
61
  .task-time{flex:none;color:var(--ink-4);font-size:var(--fs-2xs);font-variant-numeric:tabular-nums}
64
62
  .task-sub{gap:8px;height:18px;color:var(--ink-4);font-size:var(--fs-2xs)}
65
63
  .task-id{font-size:var(--fs-2xs);line-height:1;color:var(--ink-3)}
64
+ .task-source{flex:none;padding:0 5px;border:1px solid var(--line-2);border-radius:var(--r-pill);color:var(--ink-3);font-size:10px;line-height:15px}
66
65
  .task-count{display:inline-flex;align-items:center;gap:3px;line-height:1;white-space:nowrap}
67
66
  .task-count .icon{width:11px;height:11px}
68
67
  .task-signals{display:inline-flex;gap:4px;margin-left:auto}
@@ -9,8 +9,7 @@ export const TASK_STYLES = `
9
9
  and frosting the content scrolling beneath them. */
10
10
  .tabs-wrap{position:sticky;margin:0 calc(-1 * var(--gutter));padding:0 var(--gutter);background:var(--canvas)}
11
11
  .dock-toggle{height:30px}
12
- .task-bar-sep{width:1px;height:18px;margin:0 2px;background:var(--line-2)}
13
- .operator-toggle[aria-pressed="true"]{background:var(--accent-soft);color:var(--accent)}
12
+ .operator-toggle[aria-pressed="true"],.panel-toggle[aria-expanded="true"]{background:var(--accent-soft);color:var(--accent)}
14
13
  .task-hero{padding:28px 0 18px}
15
14
  .task-title{font-size:var(--fs-2xl);font-weight:650;letter-spacing:-.025em;line-height:1.25;overflow-wrap:anywhere}
16
15
  .task-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;color:var(--ink-3);font-size:var(--fs-xs)}
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_DARK_THEME } from "../../shared/themes.js";
2
2
  const SESSION_TONES = DEFAULT_DARK_THEME.tokens;
3
3
  /*
4
- * TERMINAL VIEW — the Live session pane. It renders a native terminal, so it
4
+ * TERMINAL VIEW — the session page's terminal mode. It renders a native terminal, so it
5
5
  * stays dark in every theme: its chrome uses a scoped palette (white alphas
6
6
  * and the default dark theme's status colours) instead of theme tokens.
7
7
  */
@@ -9,13 +9,15 @@ export const TERMINAL_STYLES = `
9
9
  .dock-session{background:var(--term-bg);color:#e6e6e8;--chrome-line:rgba(255,255,255,.07);--chrome-ok:${SESSION_TONES.ok};--chrome-info:${SESSION_TONES.info};--chrome-bad:${SESSION_TONES.bad}}
10
10
  .dock-session .faint{color:rgba(255,255,255,.4)}
11
11
 
12
- /* Target bar: one chip per attachable Role, then the connection state */
12
+ /* Header row: the selected Session, the connection state, the mode switch */
13
13
  .session-bar{flex:none;display:flex;align-items:center;gap:8px;min-height:44px;padding:6px 10px;border-bottom:1px solid var(--chrome-line)}
14
- .session-targets{display:flex;gap:4px;flex:1;min-width:0;overflow-x:auto;scrollbar-width:none}
15
- .target-chip{display:inline-flex;align-items:center;gap:6px;flex:none;height:26px;padding:0 10px;border:1px solid rgba(255,255,255,.12);border-radius:var(--r-pill);background:transparent;color:rgba(255,255,255,.72);font-size:var(--fs-xs);font-weight:500}
16
- .target-chip:hover{color:#fff;border-color:rgba(255,255,255,.3)}
17
- .target-chip[aria-pressed="true"]{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.3);color:#fff}
18
- .target-chip .dot{width:7px;height:7px;box-shadow:none}
14
+ .session-bar .spacer{flex:1 1 0;min-width:0}
15
+ .sp-term-icon{width:14px;height:14px;flex:none;color:rgba(255,255,255,.45)}
16
+ .sp-term-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#fff;font-size:var(--fs-sm);font-weight:600}
17
+ .dock-session .sp-modes{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.1)}
18
+ .dock-session .sp-modes .seg-btn{color:rgba(255,255,255,.62)}
19
+ .dock-session .sp-modes .seg-btn:hover{color:#fff}
20
+ .dock-session .sp-modes .seg-btn[aria-checked="true"]{background:rgba(255,255,255,.14);color:#fff;box-shadow:none}
19
21
  .conn{display:inline-flex;align-items:center;gap:6px;flex:none;color:rgba(255,255,255,.6);font-size:var(--fs-xs);font-weight:500}
20
22
  .conn i{width:7px;height:7px;border-radius:50%;background:rgba(255,255,255,.3)}
21
23
  .conn[data-state="connecting"] i{background:var(--warn);animation:m-blink 1s infinite}
@@ -33,6 +35,8 @@ export const TERMINAL_STYLES = `
33
35
  .terminal-empty .icon{color:rgba(255,255,255,.35)}
34
36
  .terminal-empty-title{color:#fff;font-weight:600}
35
37
  .terminal-empty-text{max-width:300px;font-size:var(--fs-xs);line-height:1.5}
38
+ .terminal-empty[hidden]{display:none}
39
+ .terminal-empty .btn{margin-top:4px}
36
40
  .session-foot{flex:none;display:flex;align-items:center;gap:8px;padding:8px 12px;border-top:1px solid var(--chrome-line);color:rgba(255,255,255,.45);font-size:var(--fs-2xs)}
37
- .session-foot code{color:rgba(255,255,255,.8);user-select:all}
41
+ .session-foot code{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:rgba(255,255,255,.8);user-select:all}
38
42
  `;
@@ -91,6 +91,7 @@ export function conversationSessions(set) {
91
91
  endReason: session.endReason, createdAt: session.createdAt,
92
92
  executionAuthority: session.effective.executionAuthority,
93
93
  launchModel: session.effective.model ?? null,
94
+ launchEffort: session.effective.effort ?? null,
94
95
  current: set.sessions[set.activeAgentId] === session,
95
96
  conversationEpoch: set.providerBinding?.conversations.find(c => c.conversationId === session.nativeSessionId)?.epoch ?? null }));
96
97
  }
@@ -720,7 +720,7 @@ async function handleHttpRequest(request, response, store, dependencies, token,
720
720
  try {
721
721
  const assetVersion = new URL(request.url, "http://localhost").searchParams.get("v");
722
722
  const asset = findWebAsset(pathname, assetVersion);
723
- const managementPage = /^\/(?:models(?:\/(?:providers|catalog|quota))?|settings(?:\/(?:preferences|skills|jobs|permissions|server|connection))?)\/?$/.test(pathname);
723
+ const managementPage = /^\/(?:models(?:\/(?:providers|catalog|quota))?|settings(?:\/(?:preferences|skills|jobs|permissions|server|connection))?|jobs)\/?$/.test(pathname);
724
724
  if (pathname === "/" || pathname === "/index.html" || managementPage) {
725
725
  sendText(response, 200, "text/html; charset=utf-8", prepareWebDocument(managementPage ? MODELS_HTML.replace("__YUI_WEB_TOKEN__", token) : dashboardHtml(token), managementPage ? "/assets/js/app/manage.js" : "/assets/app.js"), method === "HEAD");
726
726
  }
@@ -735,6 +735,11 @@ async function handleHttpRequest(request, response, store, dependencies, token,
735
735
  ...[...query].filter(([key]) => key !== "query").flatMap(([key, value]) => [`--${key}`, value])]);
736
736
  sendJson(response, 200, store.transaction(reader => searchTaskBodies(reader, options)), method === "HEAD");
737
737
  }
738
+ else if (pathname === "/api/jobs/scopes") {
739
+ if (new URL(request.url, "http://localhost").search !== "")
740
+ throw usageError("Job scope discovery takes no options.");
741
+ sendJson(response, 200, store.readTransaction(reader => readWebJobScopes(reader)), method === "HEAD");
742
+ }
738
743
  else if (pathname === "/api/dashboard" || pathname === "/api/dashboard/sessions") {
739
744
  const query = new URL(request.url, "http://localhost").searchParams;
740
745
  const options = parseTaskCatalogOptions([...query].flatMap(([key, value]) => key === "all"
@@ -1281,6 +1286,28 @@ function rejectUpgrade(socket, status, message) {
1281
1286
  + "Connection: close\r\n"
1282
1287
  + "Content-Length: 0\r\n\r\n");
1283
1288
  }
1289
+ /** Bounded owner discovery for the Jobs entry: counts per owning scope only.
1290
+ * Records are still listed through the scope's own Job/Schedule reads, with
1291
+ * their existing authority checks; this never lists a definition itself. */
1292
+ const JOB_SCOPE_TASK_LIMIT = 200;
1293
+ export function readWebJobScopes(reader) {
1294
+ const empty = { definitions: 0, schedules: 0, enabledSchedules: 0 };
1295
+ let global = empty;
1296
+ const tasks = [];
1297
+ for (const summary of reader.listJobScopeSummaries()) {
1298
+ const counts = { definitions: summary.definitions, schedules: summary.schedules, enabledSchedules: summary.enabledSchedules };
1299
+ if (summary.scope.kind === "global") {
1300
+ global = counts;
1301
+ continue;
1302
+ }
1303
+ const task = reader.getTask(summary.scope.taskId);
1304
+ if (task !== null)
1305
+ tasks.push({ taskId: task.id, title: task.title, status: task.status, ...counts });
1306
+ }
1307
+ tasks.sort((a, b) => Number(b.taskId.slice(5)) - Number(a.taskId.slice(5)));
1308
+ return { global, tasks: tasks.slice(0, JOB_SCOPE_TASK_LIMIT), total: tasks.length,
1309
+ omitted: Math.max(0, tasks.length - JOB_SCOPE_TASK_LIMIT), scope: "owner-counts-only; records use each scope's authorized reads" };
1310
+ }
1284
1311
  function dashboardHtml(token) {
1285
1312
  return DASHBOARD_HTML.replace('content="__YUI_WEB_TOKEN__"', `content="${escapeHtmlAttribute(token)}"`);
1286
1313
  }
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { isEnabled } from "../role/availability.js";
2
3
  import { CONFIG_DOMAINS, configDefinitionsForDomain } from "../config/configCatalog.js";
3
4
  import { effectiveConfigData, runConfigCommand } from "../commands/configCommands.js";
4
5
  import { runAgentCommand } from "../commands/agentCommands.js";
@@ -9,8 +10,8 @@ import { staticAgentConfigurationFields } from "../executor/agentConfigurationFi
9
10
  import { validateAgentLaunchConfiguration } from "../executor/agentConfigurationCatalog.js";
10
11
  import { resolveAgentProfileView } from "../profile/agentProfileRuntime.js";
11
12
  import { webLocalMutation, WebRequestRejected } from "./webMutation.js";
12
- const ROLE_NOTICE = "Saved desired configuration applies to a subsequent Role launch, not the current Session. Existing Task bindings are unchanged. No Session is restarted here.";
13
- const PROFILE_NOTICE = "Profile behavior is copied into newly created Task Roles. Inherited runtime resolves from the global Worker when applied. Existing Task Roles are not rebound.";
13
+ const ROLE_NOTICE = "Disabling blocks new execution and template selection, without stopping running Sessions or discarding input. Enable allows normal scheduling, not an immediate Session start. Existing Task bindings are unchanged. Launch settings apply to a subsequent launch; no Session is restarted here.";
14
+ const PROFILE_NOTICE = "Disabling prevents new Profile application. A Profile is a template, not a process. Existing Task Roles are not rebound or stopped. Inherited runtime requires an enabled global Worker when applied. Reset preserves enabled state.";
14
15
  const AGENT_NOTICE = "Command, arguments and environment references apply to subsequent Host processes. Authentication remains with the native Agent. Only reference names and presence in the Controller environment are shown; no environment values are returned.";
15
16
  /** A view adapter over existing commands, not another writable configuration store. */
16
17
  export function createWebSettings(store, options) {
@@ -84,6 +85,7 @@ export function createWebSettings(store, options) {
84
85
  notice = ROLE_NOTICE;
85
86
  const specs = roleOptionSpecs({ update: true, includeWorkspace: true });
86
87
  fields = [
88
+ { ...field("enabled", isEnabled(role), "boolean"), summary: ROLE_NOTICE },
87
89
  { ...field("activeAgentId", role.activeAgentId), choices: store.listConfiguredAgents().map(a => a.id),
88
90
  summary: "Explicit global Role Agent selection. Save this field alone, then edit the selected Agent's settings. Existing live-session and lifecycle guards apply; no Task is rebound." },
89
91
  field("workspace", role.workspace),
@@ -109,6 +111,7 @@ export function createWebSettings(store, options) {
109
111
  notice = PROFILE_NOTICE;
110
112
  agentId = view.runtime.status === "resolved" ? view.runtime.binding.agentId : undefined;
111
113
  fields = [
114
+ { ...field("enabled", isEnabled(profile), "boolean"), summary: PROFILE_NOTICE },
112
115
  { ...field("access", profile.defaultAccess), choices: ["read", "write"] },
113
116
  { ...field("description", profile.description ?? "", "multiline"), reset: true },
114
117
  { ...field("instructions", profile.instructions ?? "", "multiline"), reset: true },
@@ -287,6 +290,11 @@ function objectArguments(id, changes) {
287
290
  if (CONFIG_DOMAINS.includes(id))
288
291
  return [];
289
292
  return changes.flatMap(c => {
293
+ if ((id.startsWith("role/") || id.startsWith("profile/")) && c.key === "enabled") {
294
+ if (typeof c.value !== "boolean")
295
+ throw new WebRequestRejected("Enabled must be a boolean.");
296
+ return ["--enabled", String(c.value)];
297
+ }
290
298
  if (id.startsWith("role/") && c.key === "activeAgentId")
291
299
  return [];
292
300
  if (id.startsWith("agent/")) {
@@ -0,0 +1,78 @@
1
+ # Global Shell Jobs
2
+
3
+ A global Shell Job owns its command steps, environment and text script files.
4
+ It needs neither a Task nor a Project. The Web Jobs editor and CLI edit the
5
+ same revisioned definition. Saving never starts a Run.
6
+
7
+ Commands execute as the local Yui user, **not in a security sandbox**. Do not
8
+ store credentials in commands, scripts or environment variables. Task-scoped
9
+ Shell Jobs retain their existing managed Project/workspace authority.
10
+
11
+ ## CLI
12
+
13
+ Create a command-only Job:
14
+
15
+ ```sh
16
+ yui job define --global --job hello --title "Hello" --command 'printf "hello\n"'
17
+ ```
18
+
19
+ Import a local script into a Job-owned relative path (the file contents are
20
+ copied, not linked to the mutable source file):
21
+
22
+ ```sh
23
+ yui job define --global --job check --title "Daily check" \
24
+ --command 'sh scripts/check.sh' --script scripts/check.sh=./check.sh
25
+ yui job show --global --job check --json
26
+ ```
27
+
28
+ Edit the local script and upload a replacement using the revision returned by
29
+ `show`. Existing commands and other script files are preserved:
30
+
31
+ ```sh
32
+ yui job update --global --job check --revision 1 \
33
+ --script scripts/check.sh=./check.sh
34
+ yui job update --global --job hello --revision 1 --command 'printf "updated\n"'
35
+ ```
36
+
37
+ `--script` can be repeated for different paths. To edit multiple steps,
38
+ environment variables, or remove files, use `--input-file config.json` with
39
+ the complete configuration:
40
+
41
+ ```json
42
+ {
43
+ "kind": "shell",
44
+ "env": {},
45
+ "steps": [{"name": "check", "command": "sh scripts/check.sh", "timeoutMs": 60000}],
46
+ "files": [{"path": "scripts/check.sh", "content": "printf 'ok\\n'\n"}]
47
+ }
48
+ ```
49
+
50
+ Pass `--title` with complete-configuration define/update. Re-read the current
51
+ revision after a conflict instead of blindly retrying. A full configuration
52
+ replaces the previous one; shorthand edits preserve unrelated fields.
53
+
54
+ Run explicitly and inspect the exact returned Run:
55
+
56
+ ```sh
57
+ yui job run --global --job check --request-id check-20261023-1
58
+ yui job runs --global --job check
59
+ yui job logs --global --job check --run <returned-run-id>
60
+ ```
61
+
62
+ ## Files, history and schedules
63
+
64
+ Each admitted Run captures the exact configuration and script contents. The
65
+ runner materializes files in a fresh `artifacts/jobs/global/<run-id>/workspace`
66
+ directory inside the selected Yui Home. Earlier Runs are not changed by later
67
+ edits. Script paths are relative, cannot traverse directories, and are limited
68
+ to 64 files / 512 KiB total. Use `sh scripts/check.sh` or another installed
69
+ interpreter; script names do not choose an interpreter automatically.
70
+
71
+ Schedules use the existing explicit service authorization, bound to the Job's
72
+ revision. Editing commands or scripts invalidates the old schedule binding;
73
+ inspect and explicitly re-enable/re-authorize the schedule before future runs.
74
+ Saving a new script never silently changes an already authorized command.
75
+
76
+ Storage 1.23 declares this additive payload change. Upgrade an existing Home
77
+ through the normal authorized upgrade path; do not run a new build against an
78
+ older shared Home or rewrite its records by hand.
package/docs/job-runs.md CHANGED
@@ -176,9 +176,12 @@ identities. Ordinary readers use only the current contract.
176
176
  start is not confirmation. The snapshot is derived exclusively from the immutable
177
177
  Run fields, never a mutable current Job definition. Scope, executor (`shell` or
178
178
  controlled `builtin`, or `task`), target and trigger are separate concepts. Task
179
- delegation is available in either management scope; shell remains Task-only and
180
- builtin remains global-only. There is no extensible empty
181
- executor/plugin configuration or arbitrary global shell.
179
+ delegation is available in either management scope; builtin remains global-only.
180
+ Global Shell Jobs own revisioned command steps and script files, without a Task
181
+ or Project. Task Shell Jobs retain managed-workspace authority.
182
+ See [Global Shell Jobs](global-shell-jobs.md) for Web/CLI editing, per-Run file
183
+ snapshots and schedule reauthorization. There is no extensible empty executor
184
+ or plugin configuration.
182
185
 
183
186
  ## Delegating delivery to a Task
184
187