toolroll 0.9.84 → 0.9.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/README.md +94 -1193
- package/dist/agent-fence.d.ts +9 -5
- package/dist/agent-fence.js +6 -5
- package/dist/agent-onboard.d.ts +2 -0
- package/dist/agent-onboard.js +3 -0
- package/dist/assignment.js +4 -3
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +33 -31
- package/dist/browser-workspace.d.ts +45 -0
- package/dist/chat-actions.d.ts +11 -1
- package/dist/chat-actions.js +53 -1
- package/dist/chat-channel.d.ts +10 -1
- package/dist/chat-channel.js +36 -5
- package/dist/chat-decide.d.ts +16 -4
- package/dist/chat-decide.js +239 -31
- package/dist/chat-messages.d.ts +5 -0
- package/dist/chat-messages.js +1 -1
- package/dist/chat-quiet.js +6 -4
- package/dist/chat-review.d.ts +4 -0
- package/dist/chat-review.js +13 -4
- package/dist/chat-terms.d.ts +9 -0
- package/dist/chat-terms.js +46 -0
- package/dist/claim.d.ts +3 -0
- package/dist/claim.js +4 -1
- package/dist/contracts/chat-actions.d.ts +9 -0
- package/dist/contracts/chat-actions.js +3 -0
- package/dist/contracts/console-api.d.ts +6 -2
- package/dist/contracts/console-api.js +6 -2
- package/dist/contracts/flow-propose.d.ts +4 -1
- package/dist/contracts/flow-propose.js +1 -1
- package/dist/contracts/flow.d.ts +41 -0
- package/dist/contracts/flow.js +4 -2
- package/dist/contracts/lead-tools.d.ts +51 -0
- package/dist/contracts/lead-tools.js +5 -0
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/contracts/trigger-payloads.d.ts +20 -0
- package/dist/contracts/trigger-payloads.js +4 -0
- package/dist/contracts/youiyoux-request.d.ts +248 -0
- package/dist/contracts/youiyoux-request.js +152 -0
- package/dist/coordinator.js +1 -0
- package/dist/deploy-gate.js +4 -0
- package/dist/dispatch.d.ts +3 -0
- package/dist/dispatch.js +4 -1
- package/dist/flow-browser.d.ts +103 -0
- package/dist/flow-browser.js +204 -0
- package/dist/flow-cadence.d.ts +8 -2
- package/dist/flow-cadence.js +16 -3
- package/dist/flow-card-task.d.ts +9 -0
- package/dist/flow-card-task.js +15 -6
- package/dist/flow-code.d.ts +5 -1
- package/dist/flow-code.js +4 -1
- package/dist/flow-engine.js +16 -0
- package/dist/flow-live.js +9 -1
- package/dist/flow-pull-request.js +15 -7
- package/dist/flow-share.js +3 -1
- package/dist/flow-steps.d.ts +9 -1
- package/dist/flow-steps.js +55 -5
- package/dist/flow-triggers.js +55 -0
- package/dist/flows-cli.d.ts +19 -0
- package/dist/flows-cli.js +47 -2
- package/dist/flows-ui.js +11 -2
- package/dist/flows.js +5 -2
- package/dist/integrations-ui.js +9 -4
- package/dist/integrations.d.ts +9 -1
- package/dist/integrations.js +43 -2
- package/dist/landed-cleanup.d.ts +4 -0
- package/dist/landed-cleanup.js +3 -2
- package/dist/lead-capabilities.js +2 -1
- package/dist/lead-cli.js +3 -1
- package/dist/lead-tools.d.ts +2 -0
- package/dist/lead-tools.js +65 -10
- package/dist/lead-voice.js +16 -5
- package/dist/lead.d.ts +2 -0
- package/dist/lead.js +3 -3
- package/dist/operate.d.ts +2 -3
- package/dist/operate.js +226 -21
- package/dist/project-activity.d.ts +20 -0
- package/dist/project-activity.js +23 -0
- package/dist/project-cli.d.ts +1 -0
- package/dist/project-cli.js +36 -19
- package/dist/project-delete.js +7 -3
- package/dist/project-knowledge.d.ts +3 -3
- package/dist/project-outcomes.d.ts +119 -0
- package/dist/project-outcomes.js +79 -0
- package/dist/pull-request-flow.js +35 -4
- package/dist/result-actions.d.ts +4 -0
- package/dist/result-actions.js +41 -9
- package/dist/result-follow-ups.js +4 -0
- package/dist/result-review.d.ts +9 -0
- package/dist/result-review.js +45 -5
- package/dist/scope.d.ts +5 -0
- package/dist/scope.js +7 -0
- package/dist/server/chat.js +4 -2
- package/dist/server/console.css +41 -0
- package/dist/server/flows.js +15 -0
- package/dist/server/pages.js +11 -1
- package/dist/server/render-chat.js +3 -2
- package/dist/server/render-pages.d.ts +15 -2
- package/dist/server/render-pages.js +52 -4
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +20 -7
- package/dist/server/render-tasks.d.ts +9 -0
- package/dist/server/render-tasks.js +32 -11
- package/dist/server/route-table.js +12 -0
- package/dist/server/settings.js +18 -1
- package/dist/server/task-views.js +11 -0
- package/dist/server/tasks.d.ts +10 -0
- package/dist/server/tasks.js +95 -7
- package/dist/settings-proofs.js +1 -0
- package/dist/store.d.ts +116 -5
- package/dist/store.js +385 -24
- package/dist/surface.js +18 -2
- package/dist/task-archive.d.ts +10 -0
- package/dist/task-archive.js +16 -0
- package/dist/task-control.d.ts +1 -1
- package/dist/task-source.d.ts +13 -1
- package/dist/task-source.js +17 -1
- package/dist/telegram-lead.js +7 -5
- package/dist/telegram.js +1 -1
- package/dist/tidy.d.ts +130 -0
- package/dist/tidy.js +503 -0
- package/dist/toolroll-update.js +2 -2
- package/dist/webhooks.d.ts +6 -0
- package/dist/webhooks.js +25 -0
- package/dist/work-index.d.ts +28 -0
- package/dist/work-index.js +82 -28
- package/dist/workspace-ui.d.ts +1 -1
- package/dist/workspace-ui.js +3 -2
- package/dist/youiyoux-intake.d.ts +234 -0
- package/dist/youiyoux-intake.js +908 -0
- package/package.json +11 -8
package/dist/work-index.js
CHANGED
|
@@ -22,10 +22,12 @@ import { ASKS, NEED_ASK, NEEDS, askChipOf, failedAttemptSentence, processNeedOf,
|
|
|
22
22
|
import { LEAD_CLAIM_SQL, LEAD_IDLE_MS } from './lead-voice.js';
|
|
23
23
|
export const WORK_INDEX_PAGE_LIMIT = 40;
|
|
24
24
|
export const WORK_INDEX_MAX_LIMIT = 100;
|
|
25
|
-
const EMPTY = { all: 0, 'needs-you': 0, running: 0, completed: 0 };
|
|
25
|
+
const EMPTY = { all: 0, 'needs-you': 0, running: 0, completed: 0, archived: 0 };
|
|
26
26
|
const NO_GROUPS = { decide: 0, review: 0, unblock: 0, building: 0, rest: 0 };
|
|
27
27
|
const FAMILY_PROBLEM = 'Task history is unavailable or incomplete. This task is shown separately.';
|
|
28
|
-
const VIEWS = ['all', 'needs-you', 'running', 'completed'];
|
|
28
|
+
const VIEWS = ['all', 'needs-you', 'running', 'completed', 'archived'];
|
|
29
|
+
/** The family's current version is put away (v145): it waits on nobody and only the Archived view lists it. */
|
|
30
|
+
const ARCHIVED = `EXISTS(SELECT 1 FROM task_archive ta WHERE ta.task_ref=c.ref_id)`;
|
|
29
31
|
const n = (row, key) => Number(row[key] ?? 0);
|
|
30
32
|
const s = (row, key) => row[key] == null ? null : String(row[key]);
|
|
31
33
|
/** The running agent's oldest open question (agent-board.ts), from its open newest attempt only. */
|
|
@@ -93,24 +95,25 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
93
95
|
(SELECT id FROM run WHERE task_ref=a.ref_id AND outcome IS NULL ORDER BY id DESC LIMIT 1) unfinished,
|
|
94
96
|
CASE WHEN a.state='done' THEN (SELECT id FROM run WHERE task_ref=a.ref_id AND finished_at IS NOT NULL AND role IN ('builder','scout') ORDER BY id DESC LIMIT 1) END result_id,
|
|
95
97
|
(SELECT h.id FROM hold h WHERE h.task_ref=a.ref_id AND (h.until IS NULL OR h.until>$now) ORDER BY h.held_at,h.id LIMIT 1) hold_id,
|
|
96
|
-
q.decision_id,${AGENT_ASK} agent_ask,(cu.task_ref IS NOT NULL OR a.ref_id IN (SELECT value FROM json_each($workspaceCustody)))
|
|
98
|
+
q.decision_id,${AGENT_ASK} agent_ask,EXISTS(SELECT 1 FROM task_archive ta WHERE ta.task_ref=a.ref_id) member_archived,(cu.task_ref IS NOT NULL OR a.ref_id IN (SELECT value FROM json_each($workspaceCustody)))
|
|
97
99
|
AND a.ref_id NOT IN (SELECT value FROM json_each($settledCustody)) custody_unresolved
|
|
98
100
|
FROM admitted a LEFT JOIN lineage l ON l.ref_id=a.ref_id
|
|
99
101
|
LEFT JOIN questions q ON q.task_ref=a.ref_id LEFT JOIN custody cu ON cu.task_ref=a.ref_id
|
|
100
102
|
), family_heads AS MATERIALIZED (
|
|
101
103
|
SELECT root_ref,COUNT(*) version_count,MAX(updated_at) family_updated,
|
|
102
104
|
CAST(substr(MAX((CASE WHEN ref_id=root_ref THEN '0' ELSE '1' END)||created_at||printf('%020d',ref_id)),-20) AS INTEGER) current_ref,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
MAX(live_lease IS NOT NULL)
|
|
105
|
+
-- A put-away version (v145) is history: it never counts as an earlier active version or brings its question along.
|
|
106
|
+
SUM(NOT member_archived AND (state IN ('queued','running') OR live_lease IS NOT NULL OR unfinished IS NOT NULL OR custody_unresolved)) active_count,
|
|
107
|
+
MIN(CASE WHEN NOT member_archived AND (state IN ('queued','running') OR live_lease IS NOT NULL OR unfinished IS NOT NULL OR custody_unresolved) THEN ref_id END) first_active,
|
|
108
|
+
MAX(CASE WHEN NOT member_archived AND (state IN ('queued','running') OR live_lease IS NOT NULL OR unfinished IS NOT NULL OR custody_unresolved) THEN ref_id END) last_active,
|
|
109
|
+
MAX(live_lease IS NOT NULL) family_running,MIN(CASE WHEN NOT member_archived THEN decision_id END) question_id
|
|
107
110
|
FROM members GROUP BY root_ref
|
|
108
111
|
), ordered AS MATERIALIZED (
|
|
109
112
|
SELECT m.*,CASE WHEN m.ref_id=f.current_ref THEN 1 ELSE 2 END version_rank
|
|
110
113
|
FROM members m JOIN family_heads f USING(root_ref)
|
|
111
114
|
), families AS MATERIALIZED (
|
|
112
115
|
SELECT f.root_ref,f.version_count,f.family_updated,f.family_running,
|
|
113
|
-
f.active_count-(c.state IN ('queued','running') OR c.live_lease IS NOT NULL OR c.unfinished IS NOT NULL OR c.custody_unresolved) earlier_active,
|
|
116
|
+
f.active_count-(NOT c.member_archived AND (c.state IN ('queued','running') OR c.live_lease IS NOT NULL OR c.unfinished IS NOT NULL OR c.custody_unresolved)) earlier_active,
|
|
114
117
|
earlier.id earlier_id,COALESCE(c.decision_id,f.question_id) question_id
|
|
115
118
|
FROM family_heads f JOIN members c ON c.ref_id=f.current_ref
|
|
116
119
|
LEFT JOIN admitted earlier ON earlier.ref_id=CASE WHEN f.first_active=f.current_ref THEN f.last_active ELSE f.first_active END
|
|
@@ -236,8 +239,9 @@ const PROJECTION = `WITH RECURSIVE admitted AS MATERIALIZED (
|
|
|
236
239
|
SELECT c.*,CASE WHEN code IN ('cancelled','complete') THEN 0 ELSE ${LEAD_CLAIM_SQL} END lead_claim FROM classified c
|
|
237
240
|
), ranked AS MATERIALIZED (
|
|
238
241
|
-- A task the person's lead is on (lead-voice.ts) waits on nobody: it leaves Needs you until the claim ends or lapses.
|
|
239
|
-
SELECT c.*,code NOT IN ('cancelled','complete','running','updating','retry-scheduled','waiting-dependency','worker-at-capacity','planning-ready','scouting-ready','queued') AND NOT ${CUSTODY_WAITS} AND lead_claim<>1 needs,
|
|
240
|
-
code NOT IN ('cancelled','complete','running','updating','retry-scheduled','waiting-dependency','worker-at-capacity','planning-ready','scouting-ready','queued') AND NOT ${CUSTODY_WAITS} person_need,
|
|
242
|
+
SELECT c.*,code NOT IN ('cancelled','complete','running','updating','retry-scheduled','waiting-dependency','worker-at-capacity','planning-ready','scouting-ready','queued') AND NOT ${CUSTODY_WAITS} AND lead_claim<>1 AND NOT ${ARCHIVED} needs,
|
|
243
|
+
code NOT IN ('cancelled','complete','running','updating','retry-scheduled','waiting-dependency','worker-at-capacity','planning-ready','scouting-ready','queued') AND NOT ${CUSTODY_WAITS} AND NOT ${ARCHIVED} person_need,
|
|
244
|
+
${ARCHIVED} archived,
|
|
241
245
|
CASE WHEN code='complete' THEN 3 WHEN code='cancelled' THEN 4 WHEN code='running' THEN 1
|
|
242
246
|
WHEN ${CUSTODY_WAITS} OR code IN ('updating','retry-scheduled','waiting-dependency','worker-at-capacity','planning-ready','scouting-ready','queued') THEN 2 ELSE 0 END rank,
|
|
243
247
|
${ASK_RANK} ask_rank,
|
|
@@ -329,11 +333,13 @@ function parameters(now, access, options, custody) {
|
|
|
329
333
|
$leadOf: access.principal === 'operator' && access.viewer != null && access.viewer !== '' ? `lead for ${access.viewer}` : null };
|
|
330
334
|
}
|
|
331
335
|
function counts(row) {
|
|
332
|
-
return { all: n(row, 'all_count'), 'needs-you': n(row, 'needs_count'), running: n(row, 'running_count'), completed: n(row, 'completed_count') };
|
|
336
|
+
return { all: n(row, 'all_count'), 'needs-you': n(row, 'needs_count'), running: n(row, 'running_count'), completed: n(row, 'completed_count'), archived: n(row, 'archived_count') };
|
|
333
337
|
}
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
+ `COALESCE(SUM(
|
|
338
|
+
// Put-away families (v145) count only as archived.
|
|
339
|
+
const TOTALS = `COALESCE(SUM(archived=0),0) all_count,COALESCE(SUM(needs),0) needs_count,COALESCE(SUM(family_running AND archived=0),0) running_count,`
|
|
340
|
+
+ `COALESCE(SUM(code='complete' AND archived=0),0) completed_count,COALESCE(SUM(archived),0) archived_count`;
|
|
341
|
+
const GROUP_TOTALS = `COALESCE(SUM(rank=0 AND ask_rank=0 AND archived=0),0) decide_count,COALESCE(SUM(rank=0 AND ask_rank=1 AND archived=0),0) review_count,COALESCE(SUM(rank=0 AND ask_rank=2 AND archived=0),0) unblock_count,`
|
|
342
|
+
+ `COALESCE(SUM(rank=1 AND archived=0),0) building_count,COALESCE(SUM(rank>1 AND archived=0),0) rest_count`;
|
|
337
343
|
function groupCounts(row) {
|
|
338
344
|
return { decide: n(row, 'decide_count'), review: n(row, 'review_count'), unblock: n(row, 'unblock_count'), building: n(row, 'building_count'), rest: n(row, 'rest_count') };
|
|
339
345
|
}
|
|
@@ -383,18 +389,20 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
383
389
|
const view = VIEWS.includes(options.view ?? 'all') ? options.view ?? 'all' : 'all';
|
|
384
390
|
const limit = Number.isFinite(options.limit) ? Math.max(1, Math.min(WORK_INDEX_MAX_LIMIT, Math.floor(options.limit))) : WORK_INDEX_PAGE_LIMIT;
|
|
385
391
|
const scope = cursorScope(access, options, view), cursor = readCursor(options.cursor, scope);
|
|
386
|
-
const filter = view === 'needs-you' ? 'needs=1' : view === 'running' ? 'family_running=1' : view === 'completed' ? "code='complete'" : '
|
|
392
|
+
const filter = view === 'archived' ? 'archived=1' : view === 'needs-you' ? 'needs=1' : view === 'running' ? 'family_running=1 AND archived=0' : view === 'completed' ? "code='complete' AND archived=0" : 'archived=0';
|
|
387
393
|
const rows = prepared(store, `${projection}, selected_page AS MATERIALIZED (
|
|
388
|
-
SELECT * FROM ranked WHERE ${filter} AND ($taskId IS NULL OR root_ref IN (SELECT root_ref FROM members WHERE id=$taskId)) AND ($roots IS NULL OR root_id IN (SELECT value FROM json_each($roots))) AND ($cursorRoot=0 OR
|
|
394
|
+
SELECT * FROM ranked WHERE ($taskId IS NOT NULL OR ${filter}) AND ($taskId IS NULL OR root_ref IN (SELECT root_ref FROM members WHERE id=$taskId)) AND ($roots IS NULL OR root_id IN (SELECT value FROM json_each($roots))) AND ($cursorRoot=0 OR
|
|
389
395
|
${ORDER}>$cursorRank OR (${ORDER}=$cursorRank AND (sort_at<$cursorAt OR (sort_at=$cursorAt AND root_ref<$cursorRoot))))
|
|
390
396
|
ORDER BY ${ORDER},sort_at DESC,root_ref DESC LIMIT $limit
|
|
391
397
|
), page AS (SELECT p.*,COALESCE(p.result_id,(SELECT id FROM run WHERE task_ref=p.ref_id AND finished_at IS NOT NULL AND role IN ('builder','scout') ORDER BY id DESC LIMIT 1)) page_result_id FROM selected_page p), totals AS (SELECT ${TOTALS},${GROUP_TOTALS} FROM ranked), project_totals AS (
|
|
392
|
-
SELECT repo,${TOTALS},SUM(state='queued' AND family_running=0) queued,
|
|
393
|
-
SUM(state='done' AND family_running=0 AND updated_at>=$recent) done_recently FROM ranked GROUP BY repo
|
|
398
|
+
SELECT repo,${TOTALS},SUM(state='queued' AND family_running=0 AND archived=0) queued,
|
|
399
|
+
SUM(state='done' AND family_running=0 AND archived=0 AND updated_at>=$recent) done_recently FROM ranked GROUP BY repo
|
|
394
400
|
)
|
|
395
401
|
SELECT 0 record_kind,totals.*,(SELECT json_group_array(json_object('repo',repo,'all_count',all_count,'needs_count',needs_count,
|
|
396
|
-
'running_count',running_count,'completed_count',completed_count,'queued',queued,'done_recently',done_recently)) FROM project_totals) row_json FROM totals
|
|
397
|
-
UNION ALL SELECT 1,0,0,0,0,0,0,0,0,0,json_object('
|
|
402
|
+
'running_count',running_count,'completed_count',completed_count,'archived_count',archived_count,'queued',queued,'done_recently',done_recently)) FROM project_totals) row_json FROM totals
|
|
403
|
+
UNION ALL SELECT 1,0,0,0,0,0,0,0,0,0,0,json_object('archived',archived,'archived_by',(SELECT by FROM task_archive WHERE task_ref=page.ref_id),
|
|
404
|
+
'archived_kind',(SELECT by_kind FROM task_archive WHERE task_ref=page.ref_id),'archived_reason',(SELECT reason FROM task_archive WHERE task_ref=page.ref_id),
|
|
405
|
+
'archived_at',(SELECT at FROM task_archive WHERE task_ref=page.ref_id),'root_ref',root_ref,'root_id',root_id,'id',id,'root_title',root_title,'repo',repo,
|
|
398
406
|
'state',state,'created_at',created_at,'family_updated',family_updated,'version_count',version_count,'earlier_active',earlier_active,
|
|
399
407
|
'earlier_id',earlier_id,'broken',broken,'live_run',live_run,'result_id',page_result_id,'result_outcome',(SELECT outcome FROM run WHERE id=page_result_id),'attempt_reason',(SELECT substr(reason,1,1000) FROM run WHERE task_ref IN (SELECT ref_id FROM members WHERE root_ref=page.root_ref) AND (finished_at IS NOT NULL OR outcome IS NOT NULL) AND role NOT IN ('planner','reviewer') ORDER BY id DESC LIMIT 1),
|
|
400
408
|
'publication_url',(SELECT pr_url FROM publication WHERE run=page_result_id),
|
|
@@ -434,8 +442,8 @@ function readWorkIndexPage(store, now, access, options, taskId = null) {
|
|
|
434
442
|
}
|
|
435
443
|
export function workCountsByProject(store, now, access) {
|
|
436
444
|
const projection = registerValidators(store);
|
|
437
|
-
return store.handle.prepare(`${projection} SELECT repo,${TOTALS},SUM(state='queued' AND family_running=0) queued,
|
|
438
|
-
SUM(state='done' AND family_running=0 AND updated_at>=$recent) done_recently FROM ranked GROUP BY repo ORDER BY repo`)
|
|
445
|
+
return store.handle.prepare(`${projection} SELECT repo,${TOTALS},SUM(state='queued' AND family_running=0 AND archived=0) queued,
|
|
446
|
+
SUM(state='done' AND family_running=0 AND archived=0 AND updated_at>=$recent) done_recently FROM ranked GROUP BY repo ORDER BY repo`)
|
|
439
447
|
.all({ ...parameters(now, access, {}, custodyReadings(store)), $recent: new Date(now.getTime() - 86_400_000).toISOString() })
|
|
440
448
|
.map(row => ({ repo: s(row, 'repo'), totals: counts(row), queued: n(row, 'queued'), doneRecently: n(row, 'done_recently') }));
|
|
441
449
|
}
|
|
@@ -447,12 +455,44 @@ export function workStateRows(store, now, access) {
|
|
|
447
455
|
COALESCE((SELECT created_at FROM decision WHERE id=question_id),''),COALESCE((SELECT asked_at FROM agent_question WHERE id=agent_ask),''),
|
|
448
456
|
COALESCE((SELECT held_at FROM hold WHERE id=hold_id),''),COALESCE((SELECT requested_at FROM run_stop WHERE run=stop_run),'')),''),sort_at) ask_at,
|
|
449
457
|
COALESCE(live_run,(SELECT run FROM decision WHERE id=question_id),(SELECT run FROM agent_question WHERE id=agent_ask)) ask_run,
|
|
450
|
-
(SELECT json_group_array(m.ref_id) FROM members m WHERE m.root_ref=ranked.root_ref) members FROM ranked`)
|
|
458
|
+
(SELECT json_group_array(m.ref_id) FROM members m WHERE m.root_ref=ranked.root_ref) members FROM ranked WHERE archived=0`)
|
|
451
459
|
.all(parameters(now, access, {}, custodyReadings(store)))
|
|
452
460
|
.map(row => ({ repo: s(row, 'repo'), rootId: String(row['root_id']), code: String(row['code']), needs: n(row, 'needs') === 1, personNeed: n(row, 'person_need') === 1,
|
|
453
461
|
leadClaim: n(row, 'lead_claim') === 1, liveRun: row['live_run'] == null ? null : n(row, 'live_run'), unfinished: row['unfinished'] == null ? null : n(row, 'unfinished'),
|
|
454
462
|
resultRun: row['result_id'] == null ? null : n(row, 'result_id'), waits: Number(row['rank']) === 2, version: String(row['version']), at: String(row['ask_at'] ?? ''), askRun: row['ask_run'] == null ? null : n(row, 'ask_run'), members: JSON.parse(String(row['members'] ?? '[]')).map(Number) }));
|
|
455
463
|
}
|
|
464
|
+
/** Activity uses the Tasks list's exact family admission and saved completion rules.
|
|
465
|
+
* Only the requested project enters the projection. The extra row signals truncation;
|
|
466
|
+
* attempts are aggregated in SQLite, never loaded as an unbounded history. */
|
|
467
|
+
export function workOutcomeRows(store, now, access, project, since, limit) {
|
|
468
|
+
return store.handle.prepare(`${registerValidators(store)}, selected AS MATERIALIZED (
|
|
469
|
+
SELECT * FROM ranked WHERE archived=0 AND (code='ready-to-check' OR code='history-problem'
|
|
470
|
+
OR (code='complete' AND checked_at >= $since AND checked_at <= $now))
|
|
471
|
+
ORDER BY sort_at DESC,root_ref DESC LIMIT $outcomeLimit
|
|
472
|
+
), amounts AS (
|
|
473
|
+
-- reported-usage.ts runAmountOf, row for row: only evidenced billing on a run whose cost was reported is known;
|
|
474
|
+
-- an API run counts its priced spend as dollars (a safe integer), a subscription run its reported cost as usage,
|
|
475
|
+
-- never dollars. TOTAL sums as a float, so no family's sum can overflow.
|
|
476
|
+
SELECT m.root_ref,r.cost_usd,s.microusd,
|
|
477
|
+
CASE WHEN r.billing_basis IS NOT 'evidence' OR COALESCE(r.cost_unknown,1)<>0 THEN 'unknown'
|
|
478
|
+
WHEN s.billing='subscription' THEN 'subscription'
|
|
479
|
+
WHEN s.billing='api-key' AND typeof(s.microusd)='integer' AND s.microusd>=0 AND s.microusd<=9007199254740991 THEN 'api'
|
|
480
|
+
ELSE 'unknown' END amount
|
|
481
|
+
FROM selected f JOIN members m ON m.root_ref=f.root_ref JOIN run r ON r.task_ref=m.ref_id LEFT JOIN run_spend s ON s.run=r.id
|
|
482
|
+
), attempts AS (
|
|
483
|
+
SELECT root_ref,COUNT(*) runs,COUNT(CASE WHEN amount='api' THEN 1 END) api_runs,
|
|
484
|
+
TOTAL(CASE WHEN amount='api' THEN microusd END) api_microusd,
|
|
485
|
+
COUNT(CASE WHEN amount='subscription' THEN 1 END) subscription_runs,
|
|
486
|
+
SUM(CASE WHEN amount='subscription' AND typeof(cost_usd) IN ('integer','real') AND cost_usd>=0 THEN cost_usd END) subscription_usd
|
|
487
|
+
FROM amounts GROUP BY root_ref
|
|
488
|
+
) SELECT f.root_id,f.id task_id,f.root_title title,f.code,f.finished_at,f.result_id,f.result_role,
|
|
489
|
+
f.version_count,f.checked_at,f.checked_actor,f.checked_detail,
|
|
490
|
+
(SELECT lead FROM task_act WHERE task_ref=f.root_ref AND act='completed' AND at=f.checked_at ORDER BY id DESC LIMIT 1) completed_by_lead,
|
|
491
|
+
(SELECT status FROM run_check WHERE run=f.result_id) check_status,
|
|
492
|
+
COALESCE(a.runs,0) runs,COALESCE(a.api_runs,0) api_runs,a.api_microusd,COALESCE(a.subscription_runs,0) subscription_runs,a.subscription_usd
|
|
493
|
+
FROM selected f LEFT JOIN attempts a ON a.root_ref=f.root_ref ORDER BY f.sort_at DESC,f.root_ref DESC`)
|
|
494
|
+
.all({ ...parameters(now, access, { project }, custodyReadings(store)), $since: since, $outcomeLimit: limit + 1 });
|
|
495
|
+
}
|
|
456
496
|
/** Families whose headline is Ready for review, newest first: the Tasks
|
|
457
497
|
* list's own rows, so a completed family (root or any revision), a failed
|
|
458
498
|
* check and a superseded version never count. */
|
|
@@ -669,12 +709,14 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
669
709
|
if (asked !== null)
|
|
670
710
|
detail = `The agent asks: ${asked}`;
|
|
671
711
|
}
|
|
672
|
-
const
|
|
673
|
-
|
|
712
|
+
const archived = n(row, 'archived') === 1 ? { by: String(row['archived_by'] ?? ''), byKind: row['archived_kind'] === 'tidy' ? 'tidy' : 'person',
|
|
713
|
+
reason: String(row['archived_reason'] ?? ''), at: String(row['archived_at'] ?? '') } : null;
|
|
714
|
+
const views = archived !== null ? ['archived'] : ['all'];
|
|
715
|
+
if (archived === null && need)
|
|
674
716
|
views.push('needs-you');
|
|
675
|
-
if (running)
|
|
717
|
+
if (archived === null && running)
|
|
676
718
|
views.push('running');
|
|
677
|
-
if (code === 'complete')
|
|
719
|
+
if (archived === null && code === 'complete')
|
|
678
720
|
views.push('completed');
|
|
679
721
|
return { rootId: String(row['root_id']), activeTaskId: id, title: String(row['root_title']), repo: s(row, 'repo'),
|
|
680
722
|
state: String(row['state']), assignmentState, createdAt: String(row['created_at']), updatedAt: String(row['sort_at']),
|
|
@@ -686,5 +728,17 @@ function itemOf(row, principal, probe, leadName, readiness, readable = false) {
|
|
|
686
728
|
ask: need ? ASKS[n(row, 'ask_rank')] ?? 'unblock' : null,
|
|
687
729
|
chip: need ? askChipOf({ headline: shared.headline, need: shared.need?.key ?? null, planChanged, mismatch: verdict === 'refuted' && n(row, 'proof_failed_check') !== 1, ask: ASKS[n(row, 'ask_rank')] ?? 'unblock' }) : null,
|
|
688
730
|
primaryAction, completion: code === 'complete' ? { actor: String(row['checked_actor']), at: String(row['checked_at']), digest: String(row['checked_digest']), ...(landed === null ? {} : { landed }) } : null,
|
|
689
|
-
evidence: 'recorded', ...(lead === 'on-it' ? { lead: leadOnIt(leadName) } : {}) };
|
|
731
|
+
evidence: 'recorded', ...(lead === 'on-it' ? { lead: leadOnIt(leadName) } : {}), ...(archived === null ? {} : { archived }) };
|
|
732
|
+
}
|
|
733
|
+
export function workTidyRows(store, now, access) {
|
|
734
|
+
const projection = registerValidators(store);
|
|
735
|
+
return store.handle.prepare(`${projection} SELECT root_id,id,root_title,repo,state,code,archived,broken,family_updated,
|
|
736
|
+
(family_running OR live_lease IS NOT NULL OR unfinished IS NOT NULL OR custody_unresolved OR stop_run IS NOT NULL AND stop_settled IS NULL OR state='running') live,
|
|
737
|
+
(SELECT json_group_array(m.ref_id) FROM members m WHERE m.root_ref=ranked.root_ref) members,
|
|
738
|
+
(SELECT json_group_array(m.id) FROM members m WHERE m.root_ref=ranked.root_ref) member_ids FROM ranked`)
|
|
739
|
+
.all(parameters(now, access, {}, custodyReadings(store)))
|
|
740
|
+
.map(row => ({ rootId: String(row['root_id']), currentId: String(row['id']), title: String(row['root_title']), repo: s(row, 'repo'),
|
|
741
|
+
state: String(row['state']), code: String(row['code']), live: n(row, 'live') === 1, archived: n(row, 'archived') === 1, broken: n(row, 'broken') === 1,
|
|
742
|
+
updatedAt: String(row['family_updated'] ?? ''), members: JSON.parse(String(row['members'] ?? '[]')).map(Number),
|
|
743
|
+
memberIds: JSON.parse(String(row['member_ids'] ?? '[]')).map(String) }));
|
|
690
744
|
}
|
package/dist/workspace-ui.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { type ChecksFact } from "./task-status.js";
|
|
|
26
26
|
import { type Html } from "./html.js";
|
|
27
27
|
/** The Work destination's views — shortcuts over the same rows, never a
|
|
28
28
|
* persisted state. All is the default. */
|
|
29
|
-
export type WorkView = "all" | "needs-you" | "running" | "completed";
|
|
29
|
+
export type WorkView = "all" | "needs-you" | "running" | "completed" | "archived";
|
|
30
30
|
export declare const WORK_VIEWS: readonly {
|
|
31
31
|
key: WorkView;
|
|
32
32
|
label: string;
|
package/dist/workspace-ui.js
CHANGED
|
@@ -27,9 +27,10 @@ export const WORK_VIEWS = [
|
|
|
27
27
|
{ key: "needs-you", label: "Needs you", hint: "Tasks waiting on an answer, approval, or your inspection.", empty: "Nothing needs you right now. Queued and running work continues on its own." },
|
|
28
28
|
{ key: "running", label: "Building", hint: "Attempts a builder owns right now.", empty: "Nothing is building right now. Approved tasks start when a builder with capacity is connected." },
|
|
29
29
|
{ key: "completed", label: "Complete", hint: "Tasks the lead or user has marked complete. Recorded check results remain available.", empty: "No tasks have been marked complete in this view." },
|
|
30
|
+
{ key: "archived", label: "Archived", hint: "Tasks put away by you or by tidy. Their history and results are kept.", empty: "Nothing is put away." },
|
|
30
31
|
];
|
|
31
32
|
export function parseWorkView(raw) {
|
|
32
|
-
return raw === "needs-you" || raw === "running" || raw === "completed" ? raw : "all";
|
|
33
|
+
return raw === "needs-you" || raw === "running" || raw === "completed" || raw === "archived" ? raw : "all";
|
|
33
34
|
}
|
|
34
35
|
/** The review facts from the store's bounded retry projection — the same
|
|
35
36
|
* reading `diagnoseTaskDispatch` makes, so every surface starts from one
|
|
@@ -525,7 +526,7 @@ function workStatusWordsOf(facts, resultDisplay) {
|
|
|
525
526
|
}
|
|
526
527
|
/** The counts each view tab wears — from the same rows the page lists. */
|
|
527
528
|
export function workCounts(rows) {
|
|
528
|
-
const counts = { all: 0, "needs-you": 0, running: 0, completed: 0 };
|
|
529
|
+
const counts = { all: 0, "needs-you": 0, running: 0, completed: 0, archived: 0 };
|
|
529
530
|
for (const row of rows)
|
|
530
531
|
for (const view of row.views)
|
|
531
532
|
counts[view] += 1;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* youiyoux change and options requests become tasks (docs/guide/getting-started.md, "Change requests from youiyoux").
|
|
3
|
+
*
|
|
4
|
+
* youiyoux is a design-system tool: in its hub a person points at a component on a screenshot of their app and asks
|
|
5
|
+
* for a change, and it saves the request as `<project>/.youi/requests/<id>.json` (`youi.request/v1`). A project that
|
|
6
|
+
* uses youiyoux (a `youi.project.json` at its root, or a `.youi/requests` folder) has this intake on with no step of
|
|
7
|
+
* its own, unless a person turned it off in Settings → Integrations.
|
|
8
|
+
*
|
|
9
|
+
* On the worker's flow pass this reads the project's main checkout — the path from Toolroll's own records, resolved
|
|
10
|
+
* with realpath — and files each open change request with a task exactly once, keyed by the project and the request
|
|
11
|
+
* id in `youiyoux_request`: never again, whatever the file says later. Everything in the folder is outside text,
|
|
12
|
+
* written by whoever can write the project's folder:
|
|
13
|
+
*
|
|
14
|
+
* - the folders and files must be real (never a link), a file a single link to itself, at most 64 KB, read without
|
|
15
|
+
* following a link or waiting on a pipe, and at most 200 are read in one pass;
|
|
16
|
+
* - a file whose name, schema or id is wrong is counted (the integration says so) and never filed;
|
|
17
|
+
* - text that looks like a key or password is left out (recorded once, never filed), as flow triggers do;
|
|
18
|
+
* - a path the task may touch is kept only when youiyoux knew them all and every one is a plain relative path;
|
|
19
|
+
* - the task is filed through the one filing door (proposal.ts) by the "youiyoux" automation, unplanned, and waits
|
|
20
|
+
* for a person: its provenance (`filed_via = 'youiyoux'`) keeps every seal no person looked at off it (store.ts,
|
|
21
|
+
* the outside-request quarantine), so no operating mode, grant, schedule or automatic seal approves it. A person
|
|
22
|
+
* approves it in Toolroll, or with their own two taps in their paired chat where they turned approving from chat
|
|
23
|
+
* on (chat-decide.ts), bound to the exact scope the card showed.
|
|
24
|
+
*
|
|
25
|
+
* Beside each request it writes `<id>.task.json` (`youi.request-task/v1`): the task and its status, and, when the
|
|
26
|
+
* console has an address (the one the installation names, else this machine's own console where `up` serves it, on
|
|
27
|
+
* loopback), the task's link and `home`, where a person approves all waiting work. It is written when filed, whenever
|
|
28
|
+
* the status changes, and once more when that address changes. Beside an options request's options file (below), it is
|
|
29
|
+
* the only file it writes in the checkout: written whole (a temporary file in the same folder, then renamed over),
|
|
30
|
+
* refused when the folder or the target is a link, never the request itself. The status is also kept in the store;
|
|
31
|
+
* nothing Toolroll decides reads the file back.
|
|
32
|
+
*
|
|
33
|
+
* Status words (task-status.ts headline → youiyoux): Queued → queued; Planning, Building and Waiting (work going on
|
|
34
|
+
* by itself) → building; Needs you → needs-you; Ready for review → ready; Complete → done; Failed → failed;
|
|
35
|
+
* Stopped → stopped.
|
|
36
|
+
*
|
|
37
|
+
* An options request (`kind: "options"`) asks an agent for two to four options for one component, which youiyoux shows
|
|
38
|
+
* on its Screens canvas. It is filed the same way, once, waiting for a person, and only as youiyoux's contract has it:
|
|
39
|
+
* its offer names `p-<its own hex>`, that proposal's file `.youi/proposals/p-<hex>.json`, and the task may touch that
|
|
40
|
+
* one file (anything else is counted as a file that couldn't be read, and never filed). The proposal it names is kept
|
|
41
|
+
* when filed; the request is never read for it again. Then (runYouiyoux, the worker's pass):
|
|
42
|
+
*
|
|
43
|
+
* - When the task is Ready for review (or Complete), its result is read: the file at the result's commit, the commit
|
|
44
|
+
* Toolroll recorded for the build (never the checkout the agent had, which it controls and the pool hands on), read
|
|
45
|
+
* with the hardened git (teardown-git.ts). It must be a plain file in that commit (never a link), at most 64 KB, a
|
|
46
|
+
* `youi.proposal/v1` file whose id is the proposal, which answers this request, still open (an agent proposes; only a
|
|
47
|
+
* person decides), by an agent, with no key-like text. Then it is copied, byte for byte, into the project's main
|
|
48
|
+
* checkout as `.youi/proposals/<id>.json` the way the status file is written (folders real, never through a link;
|
|
49
|
+
* a temporary file, then linked into place, which never replaces a file already there: a person may have decided on
|
|
50
|
+
* it). A file missing from the result, or one that isn't safe, is said in the status file and never copied; a newer
|
|
51
|
+
* result is read again.
|
|
52
|
+
* - Only a file Toolroll copied is read for a decision: one already at that name is left as it was (a person may
|
|
53
|
+
* have decided on it) and never read for one, unless it is byte for byte the result's (an earlier pass's copy).
|
|
54
|
+
* - When that copy says a person chose an option or dismissed them all (youiyoux's hub writes that), and it is still
|
|
55
|
+
* the proposal copied (only its status, choice, decidedBy, decidedAt, request and updatedAt may differ), decided by
|
|
56
|
+
* a person, at a time, the task is marked complete by Toolroll, as landed work is (result-completion.ts LANDED_ACTOR), without
|
|
57
|
+
* merging or publishing its branch: its deliverable was the options, and youiyoux has used them. Only the exact
|
|
58
|
+
* result whose file was copied, and only through every guard a person's completion passes (landed-cleanup.ts
|
|
59
|
+
* completionProblem): otherwise it stays Ready for review and the ledger says why. The ledger keeps who decided
|
|
60
|
+
* what, as youiyoux wrote it (outside text, cleaned). A decision never approves, publishes or merges anything.
|
|
61
|
+
*/
|
|
62
|
+
import type { Store } from "./store.js";
|
|
63
|
+
import type { Headline } from "./task-status.js";
|
|
64
|
+
import { type Git } from "./landed-cleanup.js";
|
|
65
|
+
import { YOUIYOUX_FILED_VIA, type YouiyouxRequest, type YouiyouxRequestTask, type YouiyouxTaskStatus } from "./contracts/youiyoux-request.js";
|
|
66
|
+
export { YOUIYOUX_FILED_VIA };
|
|
67
|
+
/** Who files the tasks: an automation, like a flow trigger. */
|
|
68
|
+
export declare const YOUIYOUX_ACTOR = "youiyoux";
|
|
69
|
+
export declare const REQUEST_BYTES: number;
|
|
70
|
+
export declare const REQUESTS_PER_PASS = 200;
|
|
71
|
+
export declare const FILED_PER_PASS = 10;
|
|
72
|
+
/** The ledger's word for a decision youiyoux's hub made on a task's options. */
|
|
73
|
+
export declare const OPTIONS_DECIDED_ACTION = "youiyoux options decided";
|
|
74
|
+
export declare const OPTIONS_COPIED_ACTION = "youiyoux options copied";
|
|
75
|
+
/** The flow pass runs every 15 seconds; the folder is read at most this often. */
|
|
76
|
+
export declare const INTAKE_EVERY_MS = 30000;
|
|
77
|
+
export declare const STATUS_OF_HEADLINE: Readonly<Record<Headline, YouiyouxTaskStatus>>;
|
|
78
|
+
/** Whether a project uses youiyoux: a `youi.project.json` at its root, or a `.youi` folder. Only their presence is
|
|
79
|
+
* read here (a link counts, so the integration can say it is Broken); requestsFolder decides what may be read. */
|
|
80
|
+
export declare function usesYouiyoux(repo: string): boolean;
|
|
81
|
+
/** The paths a request may touch: every one a plain relative path inside the project, or none at all. */
|
|
82
|
+
export declare function safeTouches(touches: readonly string[]): string[] | null;
|
|
83
|
+
export type RequestTask = {
|
|
84
|
+
title: string;
|
|
85
|
+
goal: string;
|
|
86
|
+
outOfScope: string | null;
|
|
87
|
+
touches: string[];
|
|
88
|
+
acceptance: {
|
|
89
|
+
id: string;
|
|
90
|
+
statement: string;
|
|
91
|
+
how: null;
|
|
92
|
+
evidence: string[];
|
|
93
|
+
}[];
|
|
94
|
+
/** Where the person pointed, for the task page: the route, the component, and `file:line` when youiyoux knew a plain one. */
|
|
95
|
+
pointed: {
|
|
96
|
+
route: string;
|
|
97
|
+
component: string;
|
|
98
|
+
at: string | null;
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
/** The task a request asks for, in Toolroll's terms. Text is cleaned here; whether it may be filed is decided by the caller. */
|
|
102
|
+
export declare function requestTask(request: YouiyouxRequest): RequestTask;
|
|
103
|
+
/** A person turns the intake off or back on for a project; the ledger keeps who. */
|
|
104
|
+
export declare function setYouiyouxIntake(store: Store, repo: string, on: boolean, by: string, now: Date): void;
|
|
105
|
+
export type YouiyouxState = {
|
|
106
|
+
/** The project uses youiyoux, as the worker's last pass found. */
|
|
107
|
+
uses: boolean;
|
|
108
|
+
/** Requests become tasks: it uses youiyoux and nobody turned it off. */
|
|
109
|
+
on: boolean;
|
|
110
|
+
/** A person turned it off. */
|
|
111
|
+
off: boolean;
|
|
112
|
+
/** Filed tasks not yet done, failed or stopped. */
|
|
113
|
+
queued: number;
|
|
114
|
+
filed: number;
|
|
115
|
+
invalid: number;
|
|
116
|
+
problem: string | null;
|
|
117
|
+
checkedAt: string | null;
|
|
118
|
+
};
|
|
119
|
+
/** How the intake stands for one project, from the store alone: a page reads it on every render, so it never reads the
|
|
120
|
+
* project's folder (one that doesn't answer must not hold the console). Whether it uses youiyoux is the last pass's. */
|
|
121
|
+
export declare function youiyouxState(store: Store, repo: string): YouiyouxState;
|
|
122
|
+
/** The one line `toolroll onboard` says for a project that uses youiyoux; null when it doesn't. */
|
|
123
|
+
export declare function youiyouxOnboardLine(store: Store, repo: string): string | null;
|
|
124
|
+
/** The console the links name: the address the installation names (CONSOLE_URL or the console-url file), else, with
|
|
125
|
+
* none named, this machine's own console where `up` serves it (loopback http only, never a guessed host); null without
|
|
126
|
+
* either. Either way http(s) with no credentials, query or fragment. */
|
|
127
|
+
export declare function consoleBase(env: Record<string, string | undefined>, dir: string | null): string | null;
|
|
128
|
+
/** The task's page in that console; null without one. */
|
|
129
|
+
export declare function taskLink(env: Record<string, string | undefined>, dir: string | null, taskId: string): string | null;
|
|
130
|
+
/**
|
|
131
|
+
* Write `<id>.task.json` beside its request, whole. The folders are checked again now (a link is refused), the request
|
|
132
|
+
* must still be a plain file, the target must be a plain file or absent, and the content goes to a fresh temporary file
|
|
133
|
+
* in the same folder (created exclusively, never through a link) that is then renamed over the target.
|
|
134
|
+
*/
|
|
135
|
+
export declare function writeStatusFile(repo: string, body: YouiyouxRequestTask): {
|
|
136
|
+
ok: true;
|
|
137
|
+
} | {
|
|
138
|
+
ok: false;
|
|
139
|
+
problem: string;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Put an options file into the project's main checkout as `.youi/proposals/<id>.json`, whole, and never over a file
|
|
143
|
+
* already there (a person may have decided on it). The folders are checked as the status file's are (`.youi/proposals`
|
|
144
|
+
* is made, as a real folder, when missing), the content goes to a fresh temporary file in the same folder (created
|
|
145
|
+
* exclusively, never through a link), the folders are checked again, and the temporary file is linked into place, which
|
|
146
|
+
* fails rather than replace anything at that name, a link included. `same`: that exact content was already there;
|
|
147
|
+
* `taken`: something else was, and is left as it was.
|
|
148
|
+
*/
|
|
149
|
+
export declare function writeProposalFile(repo: string, id: string, text: string): {
|
|
150
|
+
ok: true;
|
|
151
|
+
made: "written" | "same" | "taken";
|
|
152
|
+
} | {
|
|
153
|
+
ok: false;
|
|
154
|
+
problem: string;
|
|
155
|
+
};
|
|
156
|
+
/** `git`: the hardened git an options request's result is read with (landed-cleanup.ts gitOf), and `evidenceRoot`, where
|
|
157
|
+
* results' saved evidence is (a completion is bound to the receipt read with it). Without either, options wait. */
|
|
158
|
+
export type YouiyouxIo = {
|
|
159
|
+
dir: string | null;
|
|
160
|
+
env: Record<string, string | undefined>;
|
|
161
|
+
git?: Git;
|
|
162
|
+
evidenceRoot?: string | null;
|
|
163
|
+
};
|
|
164
|
+
/** `copied`: options files that went to the hub; `decided`: decisions in the hub Toolroll acted on. */
|
|
165
|
+
export type YouiyouxPass = {
|
|
166
|
+
filed: number;
|
|
167
|
+
skipped: number;
|
|
168
|
+
invalid: number;
|
|
169
|
+
updated: number;
|
|
170
|
+
copied: number;
|
|
171
|
+
decided: number;
|
|
172
|
+
problems: string[];
|
|
173
|
+
};
|
|
174
|
+
export declare const NO_YOUIYOUX_PASS: Readonly<YouiyouxPass>;
|
|
175
|
+
/** File one request's task, exactly once: the claim and the filing are one transaction (two passes, in two processes
|
|
176
|
+
* or one, make one task). */
|
|
177
|
+
export declare function fileYouiyouxRequest(store: Store, repo: string, request: YouiyouxRequest, now: Date): {
|
|
178
|
+
made: "filed";
|
|
179
|
+
taskId: string;
|
|
180
|
+
} | {
|
|
181
|
+
made: "seen";
|
|
182
|
+
} | {
|
|
183
|
+
made: "skipped";
|
|
184
|
+
note: string;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* One message for what a pass filed, however many: the person's paired chats draw it as one card (chat-decide.ts) that
|
|
188
|
+
* lists each new task, with Approve all and each one's own Approve and Open, and a phone push links to what needs
|
|
189
|
+
* them. Keyed by the pass's first task, so a pass says it once; the tasks are the ones filed with it (one project, one
|
|
190
|
+
* moment: requestPassOf). Titles are cleaned outside text, as filed.
|
|
191
|
+
*/
|
|
192
|
+
export declare function noteYouiyouxFiled(store: Store, taskIds: readonly string[], now: Date): void;
|
|
193
|
+
/** What a pass's tasks were asked for, in a word: "change request", "options request", or "request" for both. */
|
|
194
|
+
export declare function requestKindWord(store: Store, taskIds: readonly string[]): string;
|
|
195
|
+
/** The tasks one pass filed beside this one (one project, one moment), in request order; empty for any other task. */
|
|
196
|
+
export declare function requestPassOf(store: Store, taskId: string): string[];
|
|
197
|
+
/**
|
|
198
|
+
* One project's pass: read new requests and file them, then write each filed task's status where it changed (no
|
|
199
|
+
* options moved: runYouiyoux does that too). The folder read is the project in Toolroll's records that `given`
|
|
200
|
+
* resolves to; anything else is left alone.
|
|
201
|
+
*/
|
|
202
|
+
export declare function runYouiyouxIntake(store: Store, given: string, now: Date, io: YouiyouxIo, options?: {
|
|
203
|
+
force?: boolean;
|
|
204
|
+
}): YouiyouxPass;
|
|
205
|
+
/** The worker's pass for one project (flow-cadence.ts): the intake, then each options request's options, both ways. */
|
|
206
|
+
export declare function runYouiyoux(store: Store, given: string, now: Date, io: YouiyouxIo, options?: {
|
|
207
|
+
force?: boolean;
|
|
208
|
+
}): Promise<YouiyouxPass>;
|
|
209
|
+
export type OptionsRow = {
|
|
210
|
+
request: string;
|
|
211
|
+
taskId: string;
|
|
212
|
+
proposal: string;
|
|
213
|
+
state: string | null;
|
|
214
|
+
run: number | null;
|
|
215
|
+
digest: string | null;
|
|
216
|
+
};
|
|
217
|
+
/** The digest of an options file apart from what a decision changes: the proposal itself, every other key included. */
|
|
218
|
+
export declare function proposalIdentity(raw: unknown): string | null;
|
|
219
|
+
/** Why an agent's options file can't go to the hub as it stands, or null. Toolroll checks what makes it safe to copy
|
|
220
|
+
* and this request's; youiyoux checks each option against the component's contract when it reads it. */
|
|
221
|
+
export declare function proposalProblem(text: string, proposal: string, request: string): string | null;
|
|
222
|
+
export type Decision = {
|
|
223
|
+
status: "chosen" | "dismissed";
|
|
224
|
+
choice: string | null;
|
|
225
|
+
label: string | null;
|
|
226
|
+
by: string | null;
|
|
227
|
+
at: string | null;
|
|
228
|
+
};
|
|
229
|
+
/**
|
|
230
|
+
* A person decided in the hub: mark the task complete as landed work is, bound to the exact result whose options were
|
|
231
|
+
* copied, through every guard a person's completion passes, or leave it Ready for review and say why. Once per request,
|
|
232
|
+
* in one transaction with the read. Waits (false) while the task isn't Ready, Complete or cancelled.
|
|
233
|
+
*/
|
|
234
|
+
export declare function actOnDecision(store: Store, repo: string, row: OptionsRow, decision: Decision, now: Date, root: string): boolean;
|