@hasna/loops 0.3.51 → 0.3.53
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/README.md +15 -0
- package/dist/cli/index.js +374 -27
- package/dist/daemon/index.js +61 -2
- package/dist/index.js +330 -10
- package/dist/lib/store.d.ts +1 -0
- package/dist/lib/store.js +60 -1
- package/dist/lib/templates.d.ts +13 -0
- package/dist/sdk/index.js +60 -1
- package/docs/USAGE.md +17 -0
- package/package.json +1 -1
package/dist/sdk/index.js
CHANGED
|
@@ -621,7 +621,7 @@ function writeWorkflowRunManifest(args) {
|
|
|
621
621
|
var DEFAULT_RECOVERY_BATCH_LIMIT = 100;
|
|
622
622
|
var DEFAULT_RECOVERY_SCAN_MULTIPLIER = 5;
|
|
623
623
|
var LIVE_EXPIRED_RUN_GRACE_MS = 60000;
|
|
624
|
-
var GENERATED_ROUTE_TEMPLATE_IDS = new Set(["todos-task-worker-verifier", "event-worker-verifier"]);
|
|
624
|
+
var GENERATED_ROUTE_TEMPLATE_IDS = new Set(["todos-task-worker-verifier", "task-lifecycle", "event-worker-verifier"]);
|
|
625
625
|
var GENERATED_ROUTE_KEYS = new Set(["todos-task", "generic-event"]);
|
|
626
626
|
function rowToLoop(row) {
|
|
627
627
|
return {
|
|
@@ -1565,6 +1565,65 @@ class Store {
|
|
|
1565
1565
|
throw new Error(`workflow invocation not found after create: ${id}`);
|
|
1566
1566
|
return rowToWorkflowInvocation(row);
|
|
1567
1567
|
}
|
|
1568
|
+
refreshWorkflowInvocationForWorkItem(workItemId, input) {
|
|
1569
|
+
const sourceDedupeKey = input.sourceRef.dedupeKey ?? undefined;
|
|
1570
|
+
if (!sourceDedupeKey)
|
|
1571
|
+
throw new Error("cannot refresh workflow invocation without sourceRef.dedupeKey");
|
|
1572
|
+
const now = nowIso();
|
|
1573
|
+
const claimableStatuses = ["queued", "deferred", "failed", "dead_letter", "cancelled"];
|
|
1574
|
+
const statusBindings = Object.fromEntries(claimableStatuses.map((status, index) => [`$status${index}`, status]));
|
|
1575
|
+
const placeholders = claimableStatuses.map((_, index) => `$status${index}`).join(",");
|
|
1576
|
+
const result = this.db.query(`UPDATE workflow_invocations
|
|
1577
|
+
SET workflow_id=COALESCE($workflowId, workflow_id),
|
|
1578
|
+
template_id=COALESCE($templateId, template_id),
|
|
1579
|
+
source_id=COALESCE($sourceId, source_id),
|
|
1580
|
+
source_json=$sourceJson,
|
|
1581
|
+
subject_kind=$subjectKind,
|
|
1582
|
+
subject_id=COALESCE($subjectId, subject_id),
|
|
1583
|
+
subject_path=COALESCE($subjectPath, subject_path),
|
|
1584
|
+
subject_url=COALESCE($subjectUrl, subject_url),
|
|
1585
|
+
subject_json=$subjectJson,
|
|
1586
|
+
intent=$intent,
|
|
1587
|
+
scope_json=COALESCE($scopeJson, scope_json),
|
|
1588
|
+
output_policy_json=COALESCE($outputPolicyJson, output_policy_json),
|
|
1589
|
+
updated_at=$updated
|
|
1590
|
+
WHERE source_kind=$sourceKind
|
|
1591
|
+
AND source_dedupe_key=$sourceDedupeKey
|
|
1592
|
+
AND EXISTS (
|
|
1593
|
+
SELECT 1
|
|
1594
|
+
FROM workflow_work_items
|
|
1595
|
+
WHERE id=$workItemId
|
|
1596
|
+
AND invocation_id=workflow_invocations.id
|
|
1597
|
+
AND status IN (${placeholders})
|
|
1598
|
+
)`).run({
|
|
1599
|
+
$workItemId: workItemId,
|
|
1600
|
+
$sourceKind: input.sourceRef.kind,
|
|
1601
|
+
$sourceDedupeKey: sourceDedupeKey,
|
|
1602
|
+
$workflowId: input.workflowId ?? null,
|
|
1603
|
+
$templateId: input.templateId ?? null,
|
|
1604
|
+
$sourceId: input.sourceRef.id ?? null,
|
|
1605
|
+
$sourceJson: JSON.stringify(input.sourceRef),
|
|
1606
|
+
$subjectKind: input.subjectRef.kind,
|
|
1607
|
+
$subjectId: input.subjectRef.id ?? null,
|
|
1608
|
+
$subjectPath: input.subjectRef.path ?? null,
|
|
1609
|
+
$subjectUrl: input.subjectRef.url ?? null,
|
|
1610
|
+
$subjectJson: JSON.stringify(input.subjectRef),
|
|
1611
|
+
$intent: input.intent,
|
|
1612
|
+
$scopeJson: input.scope ? JSON.stringify(input.scope) : null,
|
|
1613
|
+
$outputPolicyJson: input.outputPolicy ? JSON.stringify(input.outputPolicy) : null,
|
|
1614
|
+
$updated: now,
|
|
1615
|
+
...statusBindings
|
|
1616
|
+
});
|
|
1617
|
+
if (result.changes !== 1)
|
|
1618
|
+
throw new Error(`workflow work item is not refreshable: ${workItemId}`);
|
|
1619
|
+
const updated = this.db.query(`SELECT workflow_invocations.*
|
|
1620
|
+
FROM workflow_invocations
|
|
1621
|
+
JOIN workflow_work_items ON workflow_work_items.invocation_id = workflow_invocations.id
|
|
1622
|
+
WHERE workflow_work_items.id = ?`).get(workItemId);
|
|
1623
|
+
if (!updated)
|
|
1624
|
+
throw new Error(`workflow invocation not found after refresh for work item: ${workItemId}`);
|
|
1625
|
+
return rowToWorkflowInvocation(updated);
|
|
1626
|
+
}
|
|
1568
1627
|
getWorkflowInvocation(id) {
|
|
1569
1628
|
const row = this.db.query("SELECT * FROM workflow_invocations WHERE id = ?").get(id);
|
|
1570
1629
|
return row ? rowToWorkflowInvocation(row) : undefined;
|
package/docs/USAGE.md
CHANGED
|
@@ -345,6 +345,7 @@ into a deduped one-shot workflow loop when route capacity allows:
|
|
|
345
345
|
|
|
346
346
|
```bash
|
|
347
347
|
cat task-created-event.json | loops events handle todos-task \
|
|
348
|
+
--template task-lifecycle \
|
|
348
349
|
--provider codewith \
|
|
349
350
|
--auth-profile-pool account004,account005,account006 \
|
|
350
351
|
--permission-mode bypass \
|
|
@@ -362,6 +363,20 @@ approval-required, or `no-auto` tasks. This guard exists even when the upstream
|
|
|
362
363
|
`@hasna/events` webhook filter is misconfigured, so task existence alone is not
|
|
363
364
|
permission to execute agent work.
|
|
364
365
|
|
|
366
|
+
By default, `todos-task` routes use `todos-task-worker-verifier` for backwards
|
|
367
|
+
compatibility. Use `--template task-lifecycle` when the task should run the full
|
|
368
|
+
triage -> planner -> worker -> verifier lifecycle. The route rejects unrelated
|
|
369
|
+
workflow templates such as `pr-review` so a todos task cannot accidentally use a
|
|
370
|
+
template with the wrong contract.
|
|
371
|
+
The lifecycle template inserts deterministic gate steps after triage and after
|
|
372
|
+
planning. If either agent marks the task blocked, omits its contextual
|
|
373
|
+
`openloops:triage=go task=<id> event=<event-id>` /
|
|
374
|
+
`openloops:planner=go task=<id> event=<event-id>` marker comment, or the task
|
|
375
|
+
is marked no-auto/manual/approval-required, the next agent step is not started.
|
|
376
|
+
Use `--triage-auth-profile`, `--planner-auth-profile`,
|
|
377
|
+
`--worker-auth-profile`, and `--verifier-auth-profile` for exact Codewith role
|
|
378
|
+
profiles, or use `--auth-profile-pool` for deterministic role rotation.
|
|
379
|
+
|
|
365
380
|
Use route throttles to avoid stampeding agents when a producer creates many
|
|
366
381
|
tasks at once:
|
|
367
382
|
|
|
@@ -426,6 +441,7 @@ locks, or non-pending states stay queued in todos and are not routed:
|
|
|
426
441
|
```bash
|
|
427
442
|
loops events drain todos-task \
|
|
428
443
|
--todos-project "$HOME/.hasna/loops" \
|
|
444
|
+
--template task-lifecycle \
|
|
429
445
|
--task-list repoops-pr-queue \
|
|
430
446
|
--tags auto:route \
|
|
431
447
|
--project-path-prefix /home/hasna/workspace/hasna/opensource \
|
|
@@ -461,6 +477,7 @@ For the Hasna OSS task-created route, keep the drain deterministic and narrow:
|
|
|
461
477
|
loops routes schedule todos-task oss-task-route-drain \
|
|
462
478
|
--every 5m \
|
|
463
479
|
--todos-project "$HOME/.hasna/loops" \
|
|
480
|
+
--template task-lifecycle \
|
|
464
481
|
--project-path-prefix /home/hasna/workspace/hasna/opensource \
|
|
465
482
|
--tags auto:route \
|
|
466
483
|
--provider codewith \
|
package/package.json
CHANGED