toolroll 0.9.52 → 0.9.54
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 +16 -0
- package/README.md +55 -80
- package/dist/agentconfig.d.ts +11 -36
- package/dist/agentconfig.js +3 -62
- package/dist/approval-policy.d.ts +1 -1
- package/dist/approval-policy.js +1 -1
- package/dist/board.d.ts +3 -19
- package/dist/board.js +4 -42
- package/dist/browser/workspace.js +18 -18
- package/dist/browser-workspace.d.ts +2 -1
- package/dist/builder.d.ts +9 -40
- package/dist/builder.js +50 -257
- package/dist/chat-actions.js +3 -14
- package/dist/chat-ask.js +8 -8
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.d.ts +171 -0
- package/dist/chat-core.js +288 -0
- package/dist/chat-decide.d.ts +10 -8
- package/dist/chat-decide.js +27 -24
- package/dist/chat-delivery-state.d.ts +51 -75
- package/dist/chat-delivery-state.js +140 -170
- package/dist/chat-delivery.d.ts +21 -4
- package/dist/chat-delivery.js +313 -195
- package/dist/chat-flow.d.ts +29 -1
- package/dist/chat-flow.js +24 -50
- package/dist/chat-messages.d.ts +319 -0
- package/dist/chat-messages.js +841 -0
- package/dist/chat-migration.d.ts +30 -0
- package/dist/chat-migration.js +802 -0
- package/dist/chat-quiet.d.ts +3 -0
- package/dist/chat-quiet.js +7 -2
- package/dist/chat-task-actions.d.ts +1 -1
- package/dist/chat-task-actions.js +0 -2
- package/dist/claim.d.ts +79 -244
- package/dist/claim.js +688 -1023
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +10 -2
- package/dist/contracts/chat-content.d.ts +71 -2
- package/dist/contracts/chat-content.js +22 -3
- package/dist/contracts/chat-tables.d.ts +11 -4
- package/dist/contracts/chat-tables.js +165 -87
- package/dist/contracts/cli.d.ts +0 -13
- package/dist/contracts/cli.js +0 -2
- package/dist/contracts/console-api.d.ts +5 -10
- package/dist/contracts/console-api.js +4 -10
- package/dist/contracts/discord-callback.js +2 -1
- package/dist/contracts/flow.d.ts +50 -0
- package/dist/contracts/flow.js +26 -1
- package/dist/contracts/lead-tools.d.ts +0 -13
- package/dist/contracts/lead-tools.js +3 -3
- package/dist/contracts/slack-callback.js +3 -2
- package/dist/contracts/store-json.d.ts +1 -29
- package/dist/contracts/store-json.js +4 -17
- package/dist/contracts/teams-callback.js +2 -1
- package/dist/converse.d.ts +1 -11
- package/dist/converse.js +6 -38
- package/dist/coordinator.d.ts +0 -1
- package/dist/coordinator.js +3 -5
- package/dist/demo.js +16 -26
- package/dist/desktop-update-gate.js +0 -1
- package/dist/desktop-update.js +1 -3
- package/dist/discord-chat.js +19 -38
- package/dist/discord-settings.js +4 -13
- package/dist/discord.js +4 -14
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/dispose.d.ts +2 -3
- package/dist/dispose.js +17 -92
- package/dist/envelope.js +0 -3
- package/dist/exec.d.ts +0 -64
- package/dist/exec.js +3 -356
- package/dist/exhaustion.d.ts +22 -76
- package/dist/exhaustion.js +14 -140
- package/dist/export.js +8 -8
- package/dist/flow-engine.js +1 -1
- package/dist/flow-schedule.d.ts +154 -0
- package/dist/flow-schedule.js +321 -0
- package/dist/flow-share.js +1 -1
- package/dist/flow-triggers.d.ts +6 -1
- package/dist/flow-triggers.js +65 -10
- package/dist/flows-cli.js +8 -4
- package/dist/flows-ui.js +2 -0
- package/dist/flows.js +1 -1
- package/dist/guides.js +9 -29
- package/dist/invoke.d.ts +1 -24
- package/dist/invoke.js +17 -259
- package/dist/lead-status.js +2 -4
- package/dist/lead-voice.js +1 -1
- package/dist/mate-doors.d.ts +1 -2
- package/dist/mate-doors.js +12 -25
- package/dist/mate-tools.d.ts +2 -2
- package/dist/mate-tools.js +13 -24
- package/dist/modes.d.ts +19 -8
- package/dist/modes.js +24 -14
- package/dist/notification-rows.d.ts +30 -0
- package/dist/notification-rows.js +102 -0
- package/dist/operate-remote-arguments.js +2 -6
- package/dist/operate-remote.js +6 -7
- package/dist/operate.d.ts +0 -6
- package/dist/operate.js +226 -2248
- package/dist/phase-routing.d.ts +13 -23
- package/dist/phase-routing.js +13 -35
- package/dist/plan-auto.js +2 -2
- package/dist/plane-review.js +5 -11
- package/dist/planner-source.js +2 -1
- package/dist/policy.d.ts +1 -4
- package/dist/policy.js +0 -8
- package/dist/process-recovery-eligibility.js +2 -3
- package/dist/project-delete.js +24 -41
- package/dist/proposal.d.ts +4 -24
- package/dist/proposal.js +3 -88
- package/dist/provider-auth.d.ts +3 -3
- package/dist/provider-auth.js +4 -6
- package/dist/provider.d.ts +11 -25
- package/dist/provider.js +4 -36
- package/dist/push.js +1 -1
- package/dist/recipe-ui.js +6 -6
- package/dist/recipes.d.ts +3 -3
- package/dist/recipes.js +27 -23
- package/dist/result-shots.js +2 -2
- package/dist/retention.js +1 -1
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +1 -4
- package/dist/scope.d.ts +11 -65
- package/dist/scope.js +17 -82
- package/dist/serve.js +15 -24
- package/dist/server/chat.js +7 -30
- package/dist/server/flows.js +11 -10
- package/dist/server/guards.js +0 -1
- package/dist/server/pages-assets.js +1 -1
- package/dist/server/pages.js +0 -2
- package/dist/server/people-tokens.js +0 -4
- package/dist/server/route-table.d.ts +1 -1
- package/dist/server/route-table.js +2 -17
- package/dist/server/settings.js +6 -12
- package/dist/server/shared.d.ts +28 -212
- package/dist/server/shared.js +71 -780
- package/dist/server/task-views.js +9 -76
- package/dist/server/tasks.d.ts +1 -50
- package/dist/server/tasks.js +36 -946
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-api.d.ts +5 -1
- package/dist/slack-api.js +2 -2
- package/dist/slack-chat.d.ts +9 -6
- package/dist/slack-chat.js +29 -53
- package/dist/slack-settings.js +6 -15
- package/dist/slack.js +6 -16
- package/dist/store.d.ts +200 -1702
- package/dist/store.js +811 -5439
- package/dist/surface.js +10 -24
- package/dist/task-composer.d.ts +1 -1
- package/dist/task-composer.js +1 -1
- package/dist/task-control.d.ts +3 -10
- package/dist/task-control.js +2 -7
- package/dist/teammate-desk.js +1 -1
- package/dist/teammate-question.js +12 -12
- package/dist/teammates-ui.js +1 -1
- package/dist/teams-chat.js +12 -9
- package/dist/teams-settings.js +2 -2
- package/dist/teams.js +4 -5
- package/dist/telegram-flow.js +5 -5
- package/dist/telegram-mate.d.ts +0 -3
- package/dist/telegram-mate.js +0 -1
- package/dist/telegram.js +4 -4
- package/dist/templates.d.ts +11 -7
- package/dist/templates.js +12 -9
- package/dist/toolroll-update.d.ts +20 -0
- package/dist/toolroll-update.js +104 -5
- package/dist/work-index.js +2 -6
- package/dist/workspace-revision.js +3 -1
- package/dist/workspace-ui.js +1 -1
- package/package.json +1 -1
- package/scripts/recovery-canary.mjs +3 -3
- package/dist/contest.d.ts +0 -318
- package/dist/contest.js +0 -753
- package/dist/held.d.ts +0 -150
- package/dist/held.js +0 -799
- package/dist/liveness.d.ts +0 -30
- package/dist/liveness.js +0 -42
- package/dist/pricing.d.ts +0 -45
- package/dist/pricing.js +0 -77
- package/dist/routine.d.ts +0 -228
- package/dist/routine.js +0 -764
- package/dist/slack-state.d.ts +0 -13
- package/dist/slack-state.js +0 -10
- package/dist/supervisor.mjs +0 -297
package/dist/surface.js
CHANGED
|
@@ -13,7 +13,7 @@ import { FLOWS_DESCRIPTORS } from "./flows-cli.js";
|
|
|
13
13
|
* What the tests DO hold to the code: every root here is a routed verb
|
|
14
14
|
* and every routed verb appears here; every declared flag lives in the
|
|
15
15
|
* parser's global vocabulary with the declared arity; the subcommand
|
|
16
|
-
* inventories for task/publish/config/approver
|
|
16
|
+
* inventories for task/publish/config/approver match the
|
|
17
17
|
* exported action lists their dispatchers consult; keyed mutations
|
|
18
18
|
* declare --key; notableReasons come from DOCUMENTED_REASONS. What they
|
|
19
19
|
* CANNOT prove — that a given flag is read by a given handler — is why
|
|
@@ -243,7 +243,7 @@ const GUIDE_ROWS = [
|
|
|
243
243
|
{ invocation: "task block", synopsis: "make one task wait for another", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
244
244
|
positionals: [{ name: "id", required: true, meaning: "the waiting task" }],
|
|
245
245
|
flags: [jsonFlag, dbFlag, keyFlag, { name: "on", takesValue: true, meaning: "the blocker's id" }],
|
|
246
|
-
notableReasons: ["unknown-task"
|
|
246
|
+
notableReasons: ["unknown-task"] },
|
|
247
247
|
{ invocation: "task unblock", synopsis: "stop waiting", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
248
248
|
positionals: [{ name: "id", required: true, meaning: "the waiting task" }],
|
|
249
249
|
flags: [jsonFlag, dbFlag, keyFlag, { name: "on", takesValue: true, meaning: "the blocker's id" }],
|
|
@@ -251,7 +251,7 @@ const GUIDE_ROWS = [
|
|
|
251
251
|
{ invocation: "task next", synopsis: "move a task to the front of ITS queue (scheduling only; approval still required)", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
252
252
|
positionals: [{ name: "id", required: true, meaning: "the task" }],
|
|
253
253
|
flags: [jsonFlag, dbFlag, keyFlag, { name: "undo", takesValue: false, meaning: "back to filing order" }],
|
|
254
|
-
notableReasons: ["unknown-task", "claimed"
|
|
254
|
+
notableReasons: ["unknown-task", "claimed"] },
|
|
255
255
|
operator("task steer", "one credentialed note read before the task's next attempt — steering speaks with the operator's voice"),
|
|
256
256
|
{ invocation: "task assign", synopsis: "reserve a task for one worker, or return it to the shared queue", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
257
257
|
positionals: [{ name: "id", required: true, meaning: "the task" }],
|
|
@@ -265,9 +265,8 @@ const GUIDE_ROWS = [
|
|
|
265
265
|
{ name: "goal", takesValue: true, meaning: "what success is" },
|
|
266
266
|
{ name: "not", takesValue: true, meaning: "explicitly out of scope" },
|
|
267
267
|
{ name: "touches", takesValue: true, meaning: "comma-separated paths this may change" },
|
|
268
|
-
{ name: "budget-usd", takesValue: true, meaning: "spend ceiling for the build" },
|
|
269
|
-
|
|
270
|
-
notableReasons: ["unknown-task", "contest-open"] },
|
|
268
|
+
{ name: "budget-usd", takesValue: true, meaning: "spend ceiling for the build" }],
|
|
269
|
+
notableReasons: ["unknown-task"] },
|
|
271
270
|
{ invocation: "task plan", synopsis: "ask for a plan document (a planning session drafts; a person approves)", audience: "agent", agentMayInvoke: true, mutation: "keyed",
|
|
272
271
|
positionals: [{ name: "id", required: true, meaning: "the task" }],
|
|
273
272
|
flags: [jsonFlag, dbFlag, keyFlag,
|
|
@@ -300,7 +299,7 @@ const GUIDE_ROWS = [
|
|
|
300
299
|
notableReasons: ["usage", "unauthenticated", "not-found"] },
|
|
301
300
|
operator("task accept", "record an explicit acceptance with limitations; does not change checks or prove deployment"),
|
|
302
301
|
operator("task repair", "inspect or approve an existing historical repair draft; use task revise for new feedback"),
|
|
303
|
-
operator("task route", "which configured agents plan, build and repair this task; phase and
|
|
302
|
+
operator("task route", "which configured agents plan, build and repair this task; phase and size changes require renewed approval"),
|
|
304
303
|
operator("task reopen", "resume external work its tracker closed and has been SEEN open again"),
|
|
305
304
|
operator("task stop", "stop ONE exact live attempt (--run <id>): durable before any process is signalled, answers 'stopping' until its own processes are established gone; work, branch, and evidence preserved; no strike, no retry"),
|
|
306
305
|
operator("run settle", "last resort for a finished run whose process witness can't be proven either way: records your --why in the ledger; refuses while any of the run's processes is alive"),
|
|
@@ -312,7 +311,7 @@ const GUIDE_ROWS = [
|
|
|
312
311
|
{ name: "runner", takesValue: true, meaning: "your registered worker name" },
|
|
313
312
|
{ name: "token", takesValue: true, meaning: "the worker's token" },
|
|
314
313
|
{ name: "ttl", takesValue: true, meaning: "lease seconds" }],
|
|
315
|
-
notableReasons: ["held", "fenced", "unapproved", "reserved", "external", "
|
|
314
|
+
notableReasons: ["held", "fenced", "unapproved", "reserved", "external", "claimed", "not-ready"] },
|
|
316
315
|
{ invocation: "heartbeat", synopsis: "still working; extends the lease", audience: "agent", agentMayInvoke: true, mutation: "identity-idempotent",
|
|
317
316
|
positionals: [{ name: "lease", required: true, meaning: "the lease id" }],
|
|
318
317
|
flags: [jsonFlag, dbFlag], notableReasons: ["not-leased", "fenced"] },
|
|
@@ -338,9 +337,6 @@ const GUIDE_ROWS = [
|
|
|
338
337
|
{ invocation: "cap list", synopsis: "recorded capabilities", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag, repoFlag] },
|
|
339
338
|
{ invocation: "outbox list", synopsis: "queued notifications", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
340
339
|
{ invocation: "incident list", synopsis: "open incidents", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
341
|
-
{ invocation: "routine list", synopsis: "scheduled routines", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
342
|
-
{ invocation: "routine show", synopsis: "one routine in full", audience: "agent", agentMayInvoke: true, mutation: "none",
|
|
343
|
-
positionals: [{ name: "id", required: true, meaning: "the routine" }], flags: [jsonFlag, dbFlag] },
|
|
344
340
|
{ invocation: "config show", synopsis: "phase and spend configuration", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag, repoFlag] },
|
|
345
341
|
{ invocation: "setup show", synopsis: "installation setup", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
346
342
|
{ invocation: "verify show", synopsis: "the repository's check level, its approved full and quick commands, and a suggested quick command", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag, repoFlag] },
|
|
@@ -350,11 +346,9 @@ const GUIDE_ROWS = [
|
|
|
350
346
|
{ name: "report", takesValue: false, meaning: "record readiness observations under --runner" },
|
|
351
347
|
{ name: "runner", takesValue: true, meaning: "the registered worker name reporting" },
|
|
352
348
|
{ name: "token", takesValue: true, meaning: "that worker's token" }] },
|
|
353
|
-
{ invocation: "template list", synopsis: "
|
|
349
|
+
{ invocation: "template list", synopsis: "work templates: tasks and scheduled flows", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
354
350
|
{ invocation: "template show", synopsis: "one template", audience: "agent", agentMayInvoke: true, mutation: "none",
|
|
355
351
|
positionals: [{ name: "name", required: true, meaning: "the template" }], flags: [jsonFlag, dbFlag] },
|
|
356
|
-
{ invocation: "contest show", synopsis: "a tournament's state and results", audience: "agent", agentMayInvoke: true, mutation: "none",
|
|
357
|
-
positionals: [{ name: "id", required: true, meaning: "the contest" }], flags: [jsonFlag, dbFlag] },
|
|
358
352
|
{ invocation: "webhook status", synopsis: "connected messaging services", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
359
353
|
{ invocation: "publish status", synopsis: "publication and merge-grant state", audience: "agent", agentMayInvoke: true, mutation: "none", flags: [jsonFlag, dbFlag] },
|
|
360
354
|
// ---- operator ceremonies and infrastructure (listed, detail-free) ----
|
|
@@ -373,12 +367,6 @@ const GUIDE_ROWS = [
|
|
|
373
367
|
operator("peek", "watch live agents in the terminal — one pane per open run; --tmux opens a window per run"),
|
|
374
368
|
operator("incident resolve", "resolve an incident"),
|
|
375
369
|
operator("decide", "read and ANSWER parked decisions — answering is a person's act"),
|
|
376
|
-
operator("routine add", "file a standing order"),
|
|
377
|
-
operator("routine approve", "approve a standing order"),
|
|
378
|
-
operator("routine refresh", "re-resolve the agents a standing order freezes from today's configuration — approves nothing; approve it again afterwards"),
|
|
379
|
-
operator("routine pause", "pause a standing order"),
|
|
380
|
-
operator("routine resume", "resume a standing order"),
|
|
381
|
-
operator("routine run-now", "run a standing order immediately"),
|
|
382
370
|
operatorRead("keys status", "which provider API keys are stored — never the values"),
|
|
383
371
|
operator("keys set", "store a provider's API key as a private file — piped or --key-file, never on the command line"),
|
|
384
372
|
operator("keys clear", "remove a stored provider key (kept only as a fallback; a subscription-mode provider is unaffected)"),
|
|
@@ -414,7 +402,6 @@ const GUIDE_ROWS = [
|
|
|
414
402
|
operator("intake preview", "preview what intake would file"),
|
|
415
403
|
operator("intake pr-comments", "intake review comments from a pull request"),
|
|
416
404
|
operator("intake clear", "clear intake configuration"),
|
|
417
|
-
operator("contest exclude", "exclude a contestant from a tournament"),
|
|
418
405
|
operator("webhook primary", "choose which service receives alerts"),
|
|
419
406
|
operator("bridge", "the Telegram bridge (pairing, tokens, the follower)"),
|
|
420
407
|
operator("enroll", "grant a repository authority (backends, dispatch) — its own explicit yes"),
|
|
@@ -464,7 +451,7 @@ const REMOTE_NO = [
|
|
|
464
451
|
"memory propose",
|
|
465
452
|
];
|
|
466
453
|
const REMOTE_STEP_UP = [
|
|
467
|
-
"proposals", "decide", "task regate", "task approve", "task accept", "task merge", "run settle", "
|
|
454
|
+
"proposals", "decide", "task regate", "task approve", "task accept", "task merge", "run settle", "knowledge apply", "memory apply",
|
|
468
455
|
"flows card approve", "flows card send-back", "flows script approve",
|
|
469
456
|
"approver list", "approver add", "people list", "people invite", "people projects", "people revoke",
|
|
470
457
|
// A person's own API tokens: a password step-up on the server's machine, or the console. Never with a token.
|
|
@@ -478,9 +465,8 @@ const REMOTE_YES = [
|
|
|
478
465
|
"task state", "task block", "task unblock", "task next", "task steer", "task assign", "task scope", "task plan", "task hold", "task unhold", "task require",
|
|
479
466
|
"task requeue", "task review", "task repair", "task route", "task reopen", "task stop", "task resume",
|
|
480
467
|
"runner list", "coordinator list", "cap list", "cap add", "outbox list", "incident list", "incident resolve",
|
|
481
|
-
"routine list", "routine show", "routine add", "routine refresh", "routine pause", "routine resume", "routine run-now",
|
|
482
468
|
"config show", "verify show", "intake show", "intake run", "intake preview", "intake pr-comments", "template list", "template show",
|
|
483
|
-
"
|
|
469
|
+
"webhook status", "review show", "chat-approval show", "chat-approval off",
|
|
484
470
|
"knowledge search", "knowledge impact", "knowledge refresh",
|
|
485
471
|
"memory search", "memory decisions", "memory show", "memory decide", "memory retire", "memory review", "memory status",
|
|
486
472
|
"flows list", "flows show", "flows export", "flows create", "flows edit", "flows trigger add", "flows trigger pause", "flows trigger resume",
|
package/dist/task-composer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** UI choices translate into the same digest-bound schedules as the CLI. */
|
|
2
|
-
import { type Schedule } from "./
|
|
2
|
+
import { type Schedule } from "./flow-schedule.js";
|
|
3
3
|
export declare function composerSchedule(fields: URLSearchParams): {
|
|
4
4
|
ok: true;
|
|
5
5
|
schedule: string | null;
|
package/dist/task-composer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** UI choices translate into the same digest-bound schedules as the CLI. */
|
|
2
|
-
import { parseSchedule, scheduleText, validTimezone } from "./
|
|
2
|
+
import { parseSchedule, scheduleText, validTimezone } from "./flow-schedule.js";
|
|
3
3
|
export function composerSchedule(fields) {
|
|
4
4
|
const repeat = fields.get("repeat") ?? "once";
|
|
5
5
|
if (repeat === "once")
|
package/dist/task-control.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* the preserved draft with fresh proof.
|
|
17
17
|
*
|
|
18
18
|
* Nothing here is a second engine: the stop rides the existing run, claim,
|
|
19
|
-
* hold, recovery, invocation-gateway
|
|
19
|
+
* hold, recovery, and invocation-gateway machinery. This
|
|
20
20
|
* module only names the door and the fence.
|
|
21
21
|
*/
|
|
22
22
|
import { type DispatchDiagnosis } from "./dispatch.js";
|
|
@@ -60,13 +60,6 @@ export type StopRequest = {
|
|
|
60
60
|
runId: number;
|
|
61
61
|
by: string;
|
|
62
62
|
via: "cli" | "web" | "telegram" | "slack" | "discord" | "teams";
|
|
63
|
-
/** The held-session supervisor in this process, when there is one: a
|
|
64
|
-
* held attempt is fenced through its own handle the moment the request
|
|
65
|
-
* is durable (the lapse interval would take the same road within
|
|
66
|
-
* seconds for a request filed elsewhere). */
|
|
67
|
-
held?: {
|
|
68
|
-
stop(runId: number): Promise<void>;
|
|
69
|
-
} | undefined;
|
|
70
63
|
/** A composing confirmation transaction runs this effect only after it commits. */
|
|
71
64
|
deferSignal?: ((signal: () => void) => void) | undefined;
|
|
72
65
|
};
|
|
@@ -78,13 +71,13 @@ export type StopOutcome = {
|
|
|
78
71
|
taskId: string;
|
|
79
72
|
} | {
|
|
80
73
|
ok: false;
|
|
81
|
-
reason: "no-task" | "no-run" | "wrong-task" | "finished" | "not-live" | "
|
|
74
|
+
reason: "no-task" | "no-run" | "wrong-task" | "finished" | "not-live" | "publication";
|
|
82
75
|
detail: string;
|
|
83
76
|
};
|
|
84
77
|
/**
|
|
85
78
|
* The shared stop door. Records the durable request first (one fenced
|
|
86
79
|
* transaction: exact run, still open, this task's current claim, no
|
|
87
|
-
*
|
|
80
|
+
* admitted publication), THEN signals every child this
|
|
88
81
|
* process holds for the attempt. A worker in another process observes the
|
|
89
82
|
* same row through its stop watch within STOP_WATCH_MS.
|
|
90
83
|
*/
|
package/dist/task-control.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* the preserved draft with fresh proof.
|
|
17
17
|
*
|
|
18
18
|
* Nothing here is a second engine: the stop rides the existing run, claim,
|
|
19
|
-
* hold, recovery, invocation-gateway
|
|
19
|
+
* hold, recovery, and invocation-gateway machinery. This
|
|
20
20
|
* module only names the door and the fence.
|
|
21
21
|
*/
|
|
22
22
|
import { ownedProcessCount, runOwnerTag, terminateOwnedProcesses } from "./exec.js";
|
|
@@ -108,7 +108,7 @@ export async function underStopWatch(store, runId, body, options = {}) {
|
|
|
108
108
|
/**
|
|
109
109
|
* The shared stop door. Records the durable request first (one fenced
|
|
110
110
|
* transaction: exact run, still open, this task's current claim, no
|
|
111
|
-
*
|
|
111
|
+
* admitted publication), THEN signals every child this
|
|
112
112
|
* process holds for the attempt. A worker in another process observes the
|
|
113
113
|
* same row through its stop watch within STOP_WATCH_MS.
|
|
114
114
|
*/
|
|
@@ -123,11 +123,6 @@ export function requestTaskStop(store, request, now) {
|
|
|
123
123
|
let terminated = 0;
|
|
124
124
|
const signal = () => {
|
|
125
125
|
terminated = terminateStoppedAttempt(store, request.runId);
|
|
126
|
-
if (request.held !== undefined && store.heldSessionOf(request.runId) !== null) {
|
|
127
|
-
void request.held.stop(request.runId).catch(() => {
|
|
128
|
-
// The lapse interval retries the same fence; the durable row stands.
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
126
|
};
|
|
132
127
|
if (request.deferSignal)
|
|
133
128
|
request.deferSignal(signal);
|
package/dist/teammate-desk.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { flowCardHref, flowDefinitionOf } from "./flow-engine.js";
|
|
14
14
|
import { flowFromSteps } from "./flows.js";
|
|
15
15
|
import { addFlowTriggerTo, removeFlowTrigger, runScheduleNow, scheduleFromWords, triggerConfigOf } from "./flow-triggers.js";
|
|
16
|
-
import { describeSchedule, parseSchedule } from "./
|
|
16
|
+
import { describeSchedule, parseSchedule } from "./flow-schedule.js";
|
|
17
17
|
import { labelOf, nameOf } from "./teammate-admin.js";
|
|
18
18
|
/** The zone on a desk where the teammate handles what it's asked. */
|
|
19
19
|
export const DESK_ZONE = "handle";
|
|
@@ -30,8 +30,8 @@ const who = (store, question) => { const mate = store.getTeammate(question.teamm
|
|
|
30
30
|
/** Everyone else's buttons retire once a question is answered anywhere. */
|
|
31
31
|
function retireEverywhere(store, question, now, state) {
|
|
32
32
|
store.retireTelegramQuestion(question, now);
|
|
33
|
-
state?.prepare("UPDATE chat_question_action SET consumed=? WHERE question=? AND consumed IS NULL").run(now.toISOString(), question);
|
|
34
|
-
state?.prepare("UPDATE chat_question_prompt SET consumed=? WHERE question=? AND consumed IS NULL").run(now.toISOString(), question);
|
|
33
|
+
state?.prepare("UPDATE chat_question_action SET consumed=? WHERE provider=:provider AND question=? AND consumed IS NULL").run(now.toISOString(), question);
|
|
34
|
+
state?.prepare("UPDATE chat_question_prompt SET consumed=? WHERE provider=:provider AND question=? AND consumed IS NULL").run(now.toISOString(), question);
|
|
35
35
|
}
|
|
36
36
|
// ---- Telegram ----------------------------------------------------------------------
|
|
37
37
|
/** Buttons for the question, minted before the send (for the person it asks only); `place` stamps the message they land on. */
|
|
@@ -91,23 +91,23 @@ export function questionParts(store, notification, binding) {
|
|
|
91
91
|
}
|
|
92
92
|
/** The live buttons on one part, in the order they were minted. */
|
|
93
93
|
export function chatQuestionButtons(state, part, now) {
|
|
94
|
-
const row = state.prepare("SELECT payload FROM chat_part WHERE id=?").get(part);
|
|
94
|
+
const row = state.prepare("SELECT payload FROM chat_part WHERE provider=:provider AND id=?").get(part);
|
|
95
95
|
const content = row === undefined ? null : partContent(String(row["payload"]));
|
|
96
96
|
const labels = new Map((content?.question?.choices ?? []).map(one => [one.choice ?? "", one.label]));
|
|
97
|
-
return state.prepare("SELECT token,choice FROM chat_question_action WHERE part=? AND consumed IS NULL AND expires>? ORDER BY rowid").all(part, now.toISOString())
|
|
97
|
+
return state.prepare("SELECT token,choice FROM chat_question_action WHERE provider=:provider AND part=? AND consumed IS NULL AND expires>? ORDER BY rowid").all(part, now.toISOString())
|
|
98
98
|
.map(one => ({ token: String(one.token), label: (labels.get(one.choice ?? "") ?? IN_WORDS).slice(0, 75), words: one.choice === null }));
|
|
99
99
|
}
|
|
100
100
|
/** Show the tapped notice again with the answer, and without its buttons. */
|
|
101
101
|
function repaint(state, part, event, line) {
|
|
102
|
-
const row = state.prepare("SELECT payload FROM chat_part WHERE id=?").get(part);
|
|
102
|
+
const row = state.prepare("SELECT payload FROM chat_part WHERE provider=:provider AND id=?").get(part);
|
|
103
103
|
const before = row === undefined ? { text: "" } : partContent(String(row["payload"]));
|
|
104
104
|
const content = { text: `${before.text}\n\n${line}`.slice(0, 3400), edit: event.ts, ...(before.link === undefined ? {} : { link: before.link }), ...(before.channel === undefined ? {} : { channel: before.channel }) };
|
|
105
|
-
state.prepare("UPDATE chat_part SET payload=?,state='pending',next_at=NULL WHERE id=?").run(savedChatPart(content), part);
|
|
105
|
+
state.prepare("UPDATE chat_part SET payload=?,state='pending',next_at=NULL WHERE provider=:provider AND id=?").run(savedChatPart(content), part);
|
|
106
106
|
}
|
|
107
107
|
/** A tapped question button, inside the action's transaction; false when the token isn't one. */
|
|
108
108
|
export function applyChatQuestionTap(options, event, binding, token, now) {
|
|
109
109
|
const { store, state } = options;
|
|
110
|
-
const action = state.prepare("SELECT a
|
|
110
|
+
const action = state.prepare("SELECT a.token,a.part,a.question,a.choice,a.expires,a.consumed,p.message,e.binding AS owner,e.channel FROM chat_question_action a JOIN chat_part p ON p.provider=a.provider AND p.id=a.part JOIN chat_event e ON e.provider=p.provider AND e.id=p.event WHERE a.provider=:provider AND a.token=?").get(token);
|
|
111
111
|
if (action === undefined)
|
|
112
112
|
return false;
|
|
113
113
|
const say = (text) => state.plan(event.id, [{ text }], now);
|
|
@@ -126,9 +126,9 @@ export function applyChatQuestionTap(options, event, binding, token, now) {
|
|
|
126
126
|
}
|
|
127
127
|
if (action["choice"] === null) {
|
|
128
128
|
// One open prompt per person, across flow decisions and questions: the newest is the one the next message answers.
|
|
129
|
-
state.prepare("UPDATE chat_question_prompt SET consumed=? WHERE binding=? AND consumed IS NULL").run(now.toISOString(), binding.id);
|
|
130
|
-
state.prepare("UPDATE chat_flow_prompt SET consumed=? WHERE binding=? AND consumed IS NULL").run(now.toISOString(), binding.id);
|
|
131
|
-
state.prepare("INSERT INTO chat_question_prompt(binding,question,created,expires) VALUES(
|
|
129
|
+
state.prepare("UPDATE chat_question_prompt SET consumed=? WHERE provider=:provider AND binding=? AND consumed IS NULL").run(now.toISOString(), binding.id);
|
|
130
|
+
state.prepare("UPDATE chat_flow_prompt SET consumed=? WHERE provider=:provider AND binding=? AND consumed IS NULL").run(now.toISOString(), binding.id);
|
|
131
|
+
state.prepare("INSERT INTO chat_question_prompt(provider,id,binding,question,created,expires) VALUES(:provider,(SELECT COALESCE(MAX(id),0)+1 FROM chat_question_prompt WHERE provider=:provider),?,?,?,?)").run(binding.id, questionId, now.toISOString(), new Date(now.getTime() + PROMPT_MS).toISOString());
|
|
132
132
|
say(`Your next message here is your answer to ${who(store, question)}: “${question.question}” Send “cancel” to leave it.`);
|
|
133
133
|
return true;
|
|
134
134
|
}
|
|
@@ -149,14 +149,14 @@ export function answerChatQuestionPrompt(options, event, binding, input, now) {
|
|
|
149
149
|
if (event.channel !== binding.channel)
|
|
150
150
|
return false;
|
|
151
151
|
return store.transact(() => {
|
|
152
|
-
const prompt = state.prepare("SELECT * FROM chat_question_prompt WHERE binding=? AND consumed IS NULL AND expires>? ORDER BY id DESC LIMIT 1").get(binding.id, now.toISOString());
|
|
152
|
+
const prompt = state.prepare("SELECT * FROM chat_question_prompt WHERE provider=:provider AND binding=? AND consumed IS NULL AND expires>? ORDER BY id DESC LIMIT 1").get(binding.id, now.toISOString());
|
|
153
153
|
if (prompt === undefined)
|
|
154
154
|
return false;
|
|
155
155
|
const id = Number(prompt["id"]), questionId = Number(prompt["question"]);
|
|
156
156
|
const say = (text) => state.plan(event.id, [{ text }], now);
|
|
157
157
|
const said = input.text.trim();
|
|
158
158
|
if (/^cancel\.?$/i.test(said)) {
|
|
159
|
-
state.prepare("UPDATE chat_question_prompt SET consumed=? WHERE id=?").run(now.toISOString(), id);
|
|
159
|
+
state.prepare("UPDATE chat_question_prompt SET consumed=? WHERE provider=:provider AND id=?").run(now.toISOString(), id);
|
|
160
160
|
say("Left it unanswered. The buttons on the message still work.");
|
|
161
161
|
return true;
|
|
162
162
|
}
|
package/dist/teammates-ui.js
CHANGED
|
@@ -5,7 +5,7 @@ import { callWords, defaultRule, numberFields, receiptWords } from "./teammate-t
|
|
|
5
5
|
import { MEMORY_CHARS, searchMemories } from "./teammate-memory.js";
|
|
6
6
|
import { deskOf, localZone, routinesOf } from "./teammate-desk.js";
|
|
7
7
|
import { weekOf, weekWords } from "./teammate-week.js";
|
|
8
|
-
import { describeSchedule, parseSchedule } from "./
|
|
8
|
+
import { describeSchedule, parseSchedule } from "./flow-schedule.js";
|
|
9
9
|
/** v94: the Tools section's rule rows; a limit's fields show only while "Up to a limit" is chosen. */
|
|
10
10
|
export const TEAMMATE_CSS = `.teammate-tools .tool-grant{border-top:1px solid var(--so-line);padding-top:12px;margin-top:12px}.teammate-tools .tool-grant:first-of-type{border-top:0;margin-top:0;padding-top:0}.teammate-tools h3{font-size:1rem;margin:0 0 4px}.teammate-tools .tool-rule{display:grid;gap:6px;padding:10px 0;border-bottom:1px solid var(--so-line)}.teammate-tools .tool-rule:last-of-type{border-bottom:0}.teammate-tools .tool-rule .meta{display:block}.teammate-tools select,.teammate-tools input{max-width:100%;box-sizing:border-box}.teammate-tools .tool-limit{display:none;flex-wrap:wrap;align-items:center;gap:6px}.teammate-tools .tool-rule:has(option[value="limit"]:checked) .tool-limit{display:flex}.teammate-tools .tool-rule>select{width:auto;min-width:16em}.teammate-tools .tool-limit select{width:auto}.teammate-tools .tool-limit input{width:7em}.teammate-tools .tool-buttons{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.teammate-tools button{min-height:44px}.teammate-activity li{overflow-wrap:anywhere}.teammate-memory .memory-tell,.teammate-memory .memory-search{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin:0 0 12px}.teammate-memory .memory-tell textarea{flex:1 1 280px}.teammate-memory .memory-search input{flex:1 1 220px}.teammate-memory button{min-height:44px}.teammate-memories{list-style:none;padding:0;margin:0}.teammate-memories li{padding:10px 0;border-top:1px solid var(--so-line);overflow-wrap:anywhere}.teammate-memories li:first-child{border-top:0}.teammate-memories .meta{display:block;font-size:.85rem}.teammate-memories details.memory-edit{border:0;padding:0;margin:2px 0 0;background:transparent;box-shadow:none}.memory-edit summary{cursor:pointer;min-height:32px;display:inline-flex;align-items:center;font-size:.85rem;font-weight:400;color:var(--so-muted)}.tool-undo{border:0;padding:0;margin:8px 0;background:transparent;box-shadow:none}.tool-undo summary{cursor:pointer;min-height:36px;display:inline-flex;align-items:center;font-size:.9rem}.tool-undo-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:6px 0}.tool-undo-row select{width:auto}.teammate-overrides,.teammate-undo{padding-left:18px}.teammate-undo li{margin:6px 0}.teammate-undo form{display:inline}.teammate-week h3{font-size:1rem;margin:14px 0 4px}.teammate-week button{min-height:44px}.teammate-routines{list-style:none;padding:0;margin:8px 0}.teammate-routines li{display:grid;gap:4px;padding:10px 0;border-top:1px solid var(--so-line)}.teammate-routines li:first-child{border-top:0}.routine-buttons{display:flex;flex-wrap:wrap;gap:8px}.routine-buttons button,.routine-add button{min-height:44px}.routine-add{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin:8px 0}.routine-add label{display:grid;gap:4px;flex:1 1 200px}.routine-add label:first-of-type{flex:0 1 180px}.teammate-actions,.question-options{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0}.teammate-actions form,.question-options form{margin:0}.memory-edit form{display:grid;gap:8px;margin-top:6px}.memory-buttons{display:flex;flex-wrap:wrap;gap:8px}@media(max-width:600px){.teammate-tools select,.teammate-tools input{font-size:16px}}`;
|
|
11
11
|
const USES = [["free", "Do it"], ["limit", "Do it, up to a limit"], ["ask", "Ask first"], ["never", "Never"]];
|
package/dist/teams-chat.js
CHANGED
|
@@ -167,12 +167,12 @@ export async function deliverTeamsPart(options) {
|
|
|
167
167
|
const shared = teamsDelivery(options), state = shared.state, { store, identity } = options, now = options.clock?.() ?? new Date();
|
|
168
168
|
if (!options.current() || !state.owns(identity.installation, options.owner, now))
|
|
169
169
|
return false;
|
|
170
|
-
const row = state.
|
|
170
|
+
const row = state.nextPart(identity.installation, options.canNotify?.() !== false, now, "created");
|
|
171
171
|
if (!row)
|
|
172
172
|
return false;
|
|
173
173
|
const event = state.event(row.event), binding = event.binding === null ? null : state.bindingById(event.binding);
|
|
174
174
|
if (!binding || new Date(row.created).getTime() + 86_400_000 < now.getTime()) {
|
|
175
|
-
state.
|
|
175
|
+
state.dropPart(row.id, "Delivery expired or access changed; open the saved chat");
|
|
176
176
|
return true;
|
|
177
177
|
}
|
|
178
178
|
try {
|
|
@@ -189,7 +189,7 @@ export async function deliverTeamsPart(options) {
|
|
|
189
189
|
throw new TeamsError("Connected projects changed");
|
|
190
190
|
let text = content.text, actions = [];
|
|
191
191
|
if (content.image && content.shot && resultShotsPruned(options.evidenceRoot, content.image.run)) {
|
|
192
|
-
state.
|
|
192
|
+
state.dropPart(row.id, "Removed by retention");
|
|
193
193
|
return true;
|
|
194
194
|
}
|
|
195
195
|
if (content.image) {
|
|
@@ -206,7 +206,7 @@ export async function deliverTeamsPart(options) {
|
|
|
206
206
|
const preview = proposalPreview(store, proposal, repos, "teams");
|
|
207
207
|
text = content.phase === "armed" ? armedCardText(proposal, preview.text) : preview.text.replace("\n\nConfirm or Dismiss below. Nothing changes until you confirm.", "");
|
|
208
208
|
if (preview.buttons && preview.text.length <= PLATFORM_LIMITS.teams) {
|
|
209
|
-
actions = state.
|
|
209
|
+
actions = state.partTokens(row.id, now)
|
|
210
210
|
.map(action => ({ type: "Action.Submit", title: action.phase === "yes" ? armedYesLabel(proposal) : action.phase === "cancel" ? "Cancel" : action.phase === "dismiss" ? "Dismiss" : "Confirm", data: { so: String(action.token) } }));
|
|
211
211
|
if (!actions.length)
|
|
212
212
|
text = "This confirmation expired. Ask for a fresh proposal.";
|
|
@@ -224,7 +224,11 @@ export async function deliverTeamsPart(options) {
|
|
|
224
224
|
const asked = content.question ? chatQuestionButtons(state, row.id, now).map(one => ({ type: "Action.Submit", title: one.label.slice(0, 80), data: { so: one.token } })) : [];
|
|
225
225
|
// The lead's question to its owner: its options, then "Something else".
|
|
226
226
|
const owner = content.ask ? chatAskButtons(state, row.id, now).map(one => ({ type: "Action.Submit", title: one.label.slice(0, 80), data: { so: one.token } })) : [];
|
|
227
|
-
|
|
227
|
+
// A result's, plan's, failure's or pull request's own buttons (chat-decide.ts): its acts, then its links.
|
|
228
|
+
const decided = (content.decide?.rows.flat() ?? []).flatMap((one, index) => "token" in one
|
|
229
|
+
? [{ type: "Action.Submit", title: one.label.slice(0, 80), data: { so: one.token }, ...(index === 0 ? { style: "positive" } : {}) }]
|
|
230
|
+
: openUrlAction(options.origin(), one.link));
|
|
231
|
+
actions = [...flow, ...asked, ...owner, ...decided, ...openUrlAction(options.origin(), content.link), ...(content.also ?? []).flatMap(one => openUrlAction(options.origin(), one))];
|
|
228
232
|
}
|
|
229
233
|
const target = content.edit ?? row.message;
|
|
230
234
|
// The lead's own reply goes out in Teams' Markdown (bold anchors, labelled links); everything else stays plain.
|
|
@@ -238,17 +242,16 @@ export async function deliverTeamsPart(options) {
|
|
|
238
242
|
const messageId = target ?? (typeof answer.id === "string" ? answer.id : null);
|
|
239
243
|
if (messageId === null)
|
|
240
244
|
throw new TeamsError("Teams did not confirm the message", 15_000, true);
|
|
241
|
-
state.
|
|
245
|
+
state.partSent(row.id, messageId, now);
|
|
242
246
|
return true;
|
|
243
247
|
}
|
|
244
248
|
catch (error) {
|
|
245
249
|
if (error instanceof ChatDeliveryError && error.permanent) {
|
|
246
|
-
state.
|
|
250
|
+
state.dropPart(row.id, error.message);
|
|
247
251
|
return true;
|
|
248
252
|
}
|
|
249
253
|
const problem = error instanceof TeamsError ? error : new TeamsError("Teams delivery failed", 15_000, true);
|
|
250
|
-
state.
|
|
251
|
-
.run(problem.uncertain ? 1 : 0, new Date(now.getTime() + problem.retryMs).toISOString(), problem.message, row.id);
|
|
254
|
+
state.partFailed(row.id, { problem: problem.message, until: new Date(now.getTime() + problem.retryMs).toISOString(), uncertain: problem.uncertain }, 20);
|
|
252
255
|
return true;
|
|
253
256
|
}
|
|
254
257
|
}
|
package/dist/teams-settings.js
CHANGED
|
@@ -8,9 +8,9 @@ export function teamsSettingsHtml(store, dir, csrf, options = {}) {
|
|
|
8
8
|
const bindings = credentials ? state.bindings(credentials.installation).filter(one => state.live(one)) : [];
|
|
9
9
|
const binding = options.who === undefined ? null : bindings.find(one => one.approver === options.who) ?? null;
|
|
10
10
|
const others = bindings.length - (binding === null ? 0 : 1);
|
|
11
|
-
const runtime = credentials ? state.
|
|
11
|
+
const runtime = credentials ? state.runtime(credentials.installation) : null;
|
|
12
12
|
const now = options.now ?? new Date();
|
|
13
|
-
const live = runtime &&
|
|
13
|
+
const live = runtime && runtime.leaseUntil !== null && runtime.leaseUntil > now.toISOString() && runtime.connected;
|
|
14
14
|
const hidden = `<input type="hidden" name="csrf" value="${escape(csrf)}">`;
|
|
15
15
|
const password = '<label>Your Toolroll password<input style="min-height:44px" type="password" name="password" autocomplete="current-password" required></label>';
|
|
16
16
|
const post = (action, content) => `<form method="post" action="/settings/teams/${action}" class="card">${hidden}${content}</form>`;
|
package/dist/teams.js
CHANGED
|
@@ -93,7 +93,7 @@ export async function followTeams(options) {
|
|
|
93
93
|
const current = loadTeamsCredentials(options.dir);
|
|
94
94
|
return alive && !options.signal.aborted && current?.installation === credentials.installation && current.secret === credentials.secret && state.owns(credentials.installation, owner);
|
|
95
95
|
};
|
|
96
|
-
const problem = (message) => state.
|
|
96
|
+
const problem = (message) => state.setProblem(credentials.installation, owner, message);
|
|
97
97
|
const stop = () => { alive = false; };
|
|
98
98
|
options.signal.addEventListener("abort", stop, { once: true });
|
|
99
99
|
const heartbeat = setInterval(() => { if (!same() || !state.lease(credentials.installation, owner, new Date()))
|
|
@@ -103,7 +103,7 @@ export async function followTeams(options) {
|
|
|
103
103
|
// Connected means the app can sign in; inbound reachability is the operator's endpoint to verify.
|
|
104
104
|
try {
|
|
105
105
|
await teamsAccessToken(credentials, options.fetcher);
|
|
106
|
-
state.
|
|
106
|
+
state.setConnected(credentials.installation, owner, new Date());
|
|
107
107
|
}
|
|
108
108
|
catch (error) {
|
|
109
109
|
problem(error instanceof TeamsError ? error.message : "Microsoft sign-in failed. Check the Teams app credentials.");
|
|
@@ -111,8 +111,7 @@ export async function followTeams(options) {
|
|
|
111
111
|
continue;
|
|
112
112
|
}
|
|
113
113
|
while (same()) {
|
|
114
|
-
|
|
115
|
-
if (typeof retry === "string" && retry > new Date().toISOString()) {
|
|
114
|
+
if (state.retryAt(credentials.installation) > new Date().toISOString()) {
|
|
116
115
|
await pause(1000);
|
|
117
116
|
continue;
|
|
118
117
|
}
|
|
@@ -134,7 +133,7 @@ export async function followTeams(options) {
|
|
|
134
133
|
clearInterval(heartbeat);
|
|
135
134
|
alive = false;
|
|
136
135
|
options.signal.removeEventListener("abort", stop);
|
|
137
|
-
state.
|
|
136
|
+
state.stop(credentials.installation, owner);
|
|
138
137
|
}
|
|
139
138
|
await pause(5000);
|
|
140
139
|
}
|
package/dist/telegram-flow.js
CHANGED
|
@@ -180,7 +180,7 @@ export function applyFlowTap(store, binding, action, message, repos, now) {
|
|
|
180
180
|
store.retireTelegramFlowVisit(action.card, action.entry, now);
|
|
181
181
|
const mint = (phase) => {
|
|
182
182
|
const token = randomBytes(16).toString("hex");
|
|
183
|
-
store.
|
|
183
|
+
store.telegramChat().prepare("INSERT INTO chat_flow_action (provider, token, binding, chat, message, card, entry, action, created, expires) VALUES (:provider,?,?,?,?,?,?,?,?,?)")
|
|
184
184
|
.run(token, binding.id, binding.chatId, message.messageId, action.card, action.entry, phase, now.toISOString(), new Date(now.getTime() + CONFIRM_TTL_MS).toISOString());
|
|
185
185
|
return token;
|
|
186
186
|
};
|
|
@@ -201,17 +201,17 @@ export function applyFlowTap(store, binding, action, message, repos, now) {
|
|
|
201
201
|
export function flowConfirmOf(store, token) {
|
|
202
202
|
if (!/^[0-9a-f]{32}$/.test(token))
|
|
203
203
|
return null;
|
|
204
|
-
const row = store.
|
|
204
|
+
const row = store.telegramChat().prepare("SELECT * FROM chat_flow_action WHERE provider = :provider AND token = ? AND action IN ('yes','cancel')").get(token);
|
|
205
205
|
if (row === undefined)
|
|
206
206
|
return null;
|
|
207
|
-
return { token: String(row["token"]), binding: Number(row["binding"]), chatId: String(row["
|
|
208
|
-
phase: String(row["
|
|
207
|
+
return { token: String(row["token"]), binding: Number(row["binding"]), chatId: String(row["chat"]), messageId: String(row["message"]), card: Number(row["card"]), entry: Number(row["entry"]),
|
|
208
|
+
phase: String(row["action"]) === "cancel" ? "cancel" : "yes", expiresAt: String(row["expires"]), consumedAt: row["consumed"] == null ? null : String(row["consumed"]) };
|
|
209
209
|
}
|
|
210
210
|
/** The second tap, inside the update's transaction: Yes decides the card through the console's door, once; Cancel puts
|
|
211
211
|
* the card's own buttons back. The caller has proved the binding, chat and message are the ones it was armed on. */
|
|
212
212
|
export function applyFlowConfirm(store, binding, confirm, message, repos, now) {
|
|
213
213
|
const body = message.text.replace(FLOW_QUESTION, "");
|
|
214
|
-
const spend = () => store.
|
|
214
|
+
const spend = () => store.telegramChat().prepare("UPDATE chat_flow_action SET consumed = ? WHERE provider = :provider AND action IN ('yes','cancel') AND card = ? AND entry = ? AND message = ? AND consumed IS NULL").run(now.toISOString(), confirm.card, confirm.entry, confirm.messageId);
|
|
215
215
|
if (confirm.consumedAt !== null || confirm.expiresAt <= now.toISOString()) {
|
|
216
216
|
spend();
|
|
217
217
|
const now_ = flowWhereNow(store, confirm.card, repos);
|
package/dist/telegram-mate.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type PhoneLink } from "./chat-channel.js";
|
|
2
2
|
export { channelRepos as telegramConversationRepos, resolveChannelMate as resolveTelegramMate, tooLongText, whichTaskText, replyContextFor, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText, CHAT_ACTION_PARITY as TELEGRAM_ACTION_PARITY, parityGaps, type PhoneLink, type ResolvedMate, type ParitySupport } from "./chat-channel.js";
|
|
3
|
-
import { type DoorOptions } from "./mate-doors.js";
|
|
4
3
|
import { type MateAsk, type Store, type TelegramBinding } from "./store.js";
|
|
5
4
|
import type { SubscriptionMateRunner } from "./subscription-chat.js";
|
|
6
5
|
import type { TelegramTransport } from "./telegram.js";
|
|
@@ -20,8 +19,6 @@ export type TelegramConversationOptions = {
|
|
|
20
19
|
evidenceRoot: string;
|
|
21
20
|
/** Injected by tests; production invokes the isolated local harness. */
|
|
22
21
|
subscriptionRunner?: SubscriptionMateRunner;
|
|
23
|
-
/** The held-session supervisor in this process, when there is one (a stop fences through it). */
|
|
24
|
-
held?: DoorOptions["held"];
|
|
25
22
|
/**
|
|
26
23
|
* The https origin a phone link may open, read again on EVERY call (a
|
|
27
24
|
* card is linked immediately before it is sent or edited, never from a
|
package/dist/telegram-mate.js
CHANGED
|
@@ -277,7 +277,6 @@ export function applyProposalTap(store, binding, token, message, repos, options,
|
|
|
277
277
|
via: "telegram",
|
|
278
278
|
evidenceRoot: options.evidenceRoot,
|
|
279
279
|
confirm: action.phase === "yes",
|
|
280
|
-
...(options.held === undefined ? {} : { held: options.held }),
|
|
281
280
|
deferSignal: signal => effects.push({ kind: "signal", run: signal }),
|
|
282
281
|
});
|
|
283
282
|
if (!outcome.ok && outcome.reason === "needs-confirm") {
|
package/dist/telegram.js
CHANGED
|
@@ -634,7 +634,7 @@ function decideOfferFor(store, binding, target, projects, now, root) {
|
|
|
634
634
|
if (target === null || target === undefined)
|
|
635
635
|
return null;
|
|
636
636
|
const principal = verifyApproverStanding(store, binding.approver, binding.approverGeneration, telegramConversationRepos(store, binding.approver, projects));
|
|
637
|
-
return principal.ok ? decideOffer(store, target, principal.who, now,
|
|
637
|
+
return principal.ok ? decideOffer(store, target, principal.who, now, "telegram", root) : null;
|
|
638
638
|
}
|
|
639
639
|
/** Team-conversation traffic to the chats that follow one — after the outbox, under the same delivery switch, never a problem to raise when nothing follows anything. */
|
|
640
640
|
async function deliverTeam(store, botId, transport, clock, report, readProjects, canDeliver, phoneOrigin) {
|
|
@@ -2518,9 +2518,9 @@ function noteDigestOf(note) {
|
|
|
2518
2518
|
* machine — "never silently answer without the expected note").
|
|
2519
2519
|
*/
|
|
2520
2520
|
function expiredDraftGuard(store, binding, decisionId, now) {
|
|
2521
|
-
const expired = store.
|
|
2522
|
-
.prepare(`SELECT id FROM
|
|
2523
|
-
WHERE binding = ? AND decision = ? AND state IN ('pending','armed') AND
|
|
2521
|
+
const expired = store.telegramChat()
|
|
2522
|
+
.prepare(`SELECT id FROM chat_note_draft
|
|
2523
|
+
WHERE provider = :provider AND binding = ? AND decision = ? AND state IN ('pending','armed') AND expires <= ?`)
|
|
2524
2524
|
.get(binding.id, decisionId, now.toISOString());
|
|
2525
2525
|
if (expired === undefined)
|
|
2526
2526
|
return false;
|
package/dist/templates.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The template library (adoption track, step 2) — common
|
|
3
|
-
*
|
|
2
|
+
* The template library (adoption track, step 2) — common work shipped as
|
|
3
|
+
* STATIC DATA that renders into editable, UNAPPROVED drafts.
|
|
4
4
|
*
|
|
5
5
|
* What a template is: a pre-filled form. What it is not: automation with
|
|
6
6
|
* authority. Applying one produces exactly the rows a manual filing
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* capitals, because `--file` must never read as a yes.
|
|
10
10
|
*
|
|
11
11
|
* The Codex adoption review (finding 10) drew the v1 line: templates may
|
|
12
|
-
* express ONLY what a task or
|
|
12
|
+
* express ONLY what a task proposal or a scheduled flow can carry today. A
|
|
13
|
+
* repeating template (v115: what a routine template was) makes a scheduled
|
|
14
|
+
* flow whose schedule starts paused, and whose every firing waits for
|
|
15
|
+
* approval under the project's rules. The two
|
|
13
16
|
* use cases that imply separate authenticated authority — GitHub issue
|
|
14
17
|
* intake (a grant) and CI babysitting (publish + the repair button) —
|
|
15
18
|
* ship as RECIPES: they display the existing ceremonies, and cannot be
|
|
@@ -35,11 +38,12 @@ export type TaskTemplate = {
|
|
|
35
38
|
/** The fields most installations change — shown in `show` and preview. */
|
|
36
39
|
edit: string[];
|
|
37
40
|
};
|
|
38
|
-
export type
|
|
39
|
-
kind: "
|
|
41
|
+
export type ScheduledTemplate = {
|
|
42
|
+
kind: "scheduled";
|
|
40
43
|
name: string;
|
|
41
44
|
purpose: string;
|
|
42
|
-
|
|
45
|
+
/** The flow's name. */
|
|
46
|
+
title: string;
|
|
43
47
|
goal: string;
|
|
44
48
|
outOfScope: string | null;
|
|
45
49
|
touches: string[];
|
|
@@ -60,6 +64,6 @@ export type RecipeTemplate = {
|
|
|
60
64
|
run: string;
|
|
61
65
|
}[];
|
|
62
66
|
};
|
|
63
|
-
export type Template = TaskTemplate |
|
|
67
|
+
export type Template = TaskTemplate | ScheduledTemplate | RecipeTemplate;
|
|
64
68
|
export declare const TEMPLATES: readonly Template[];
|
|
65
69
|
export declare function templateByName(name: string): Template | null;
|