cloving 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +251 -20
- package/assets/watch-fonts/fira-code-OFL.md +93 -0
- package/assets/watch-fonts/fira-code-latin-400-normal.woff2 +0 -0
- package/assets/watch-fonts/fira-code-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-OFL.md +93 -0
- package/assets/watch-fonts/heebo-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-latin-700-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-latin-800-normal.woff2 +0 -0
- package/assets/watch-fonts/manifest.json +112 -0
- package/assets/watch-fonts/space-grotesk-OFL.md +93 -0
- package/assets/watch-fonts/space-grotesk-latin-400-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-600-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-700-normal.woff2 +0 -0
- package/dist/src/authority/commands.d.ts +1 -0
- package/dist/src/authority/commands.js +33 -4
- package/dist/src/authority/dispatch-decision.d.ts +1 -0
- package/dist/src/authority/dispatch-decision.js +7 -4
- package/dist/src/authority/events.js +2 -2
- package/dist/src/authority/handoff-chain.d.ts +24 -0
- package/dist/src/authority/handoff-chain.js +46 -0
- package/dist/src/authority/reconciliation.d.ts +3 -0
- package/dist/src/authority/reconciliation.js +21 -5
- package/dist/src/authority/transitions.d.ts +1 -1
- package/dist/src/authority/transitions.js +5 -0
- package/dist/src/broker/actor/authority-queries.d.ts +4 -0
- package/dist/src/broker/actor/authority-queries.js +51 -2
- package/dist/src/broker/actor/authority-session.d.ts +2 -0
- package/dist/src/broker/actor/authority-session.js +3 -4
- package/dist/src/broker/actor/delivery-intents.d.ts +1 -0
- package/dist/src/broker/actor/effect-delivery.js +9 -0
- package/dist/src/broker/authority-admission.js +13 -5
- package/dist/src/chats/analysis-batches.d.ts +42 -0
- package/dist/src/chats/analysis-batches.js +97 -0
- package/dist/src/chats/analysis-checkpoint.d.ts +29 -0
- package/dist/src/chats/analysis-checkpoint.js +31 -0
- package/dist/src/chats/analysis-contract.d.ts +80 -0
- package/dist/src/chats/analysis-contract.js +16 -0
- package/dist/src/chats/analysis-data.d.ts +3 -0
- package/dist/src/chats/analysis-data.js +85 -0
- package/dist/src/chats/analysis-input.d.ts +18 -0
- package/dist/src/chats/analysis-input.js +97 -0
- package/dist/src/chats/analysis-links.d.ts +2 -0
- package/dist/src/chats/analysis-links.js +25 -0
- package/dist/src/chats/analysis-merge.d.ts +16 -0
- package/dist/src/chats/analysis-merge.js +134 -0
- package/dist/src/chats/analysis-prompt.d.ts +18 -0
- package/dist/src/chats/analysis-prompt.js +42 -0
- package/dist/src/chats/analysis-proposal.d.ts +4 -0
- package/dist/src/chats/analysis-proposal.js +41 -0
- package/dist/src/chats/analysis-provenance.d.ts +4 -0
- package/dist/src/chats/analysis-provenance.js +99 -0
- package/dist/src/chats/analysis-shape.d.ts +4 -0
- package/dist/src/chats/analysis-shape.js +235 -0
- package/dist/src/chats/analysis.d.ts +3 -0
- package/dist/src/chats/analysis.js +42 -0
- package/dist/src/chats/corpus-input.d.ts +5 -0
- package/dist/src/chats/corpus-input.js +288 -0
- package/dist/src/chats/destination.js +3 -1
- package/dist/src/chats/import.d.ts +8 -6
- package/dist/src/chats/import.js +195 -23
- package/dist/src/chats/input-file.d.ts +3 -0
- package/dist/src/chats/input-file.js +32 -0
- package/dist/src/chats/normalize.d.ts +11 -2
- package/dist/src/chats/normalize.js +18 -2
- package/dist/src/chats/previous-import.d.ts +5 -0
- package/dist/src/chats/previous-import.js +34 -0
- package/dist/src/chats/reader.d.ts +7 -1
- package/dist/src/chats/reader.js +46 -15
- package/dist/src/chats/redact-import.d.ts +14 -0
- package/dist/src/chats/redact-import.js +141 -0
- package/dist/src/chats/redaction.d.ts +41 -0
- package/dist/src/chats/redaction.js +161 -0
- package/dist/src/chats/reference-mapping.d.ts +16 -0
- package/dist/src/chats/reference-mapping.js +153 -0
- package/dist/src/chats/revision-input.d.ts +3 -0
- package/dist/src/chats/revision-input.js +180 -0
- package/dist/src/chats/revision.d.ts +48 -0
- package/dist/src/chats/revision.js +94 -0
- package/dist/src/chats/selection.d.ts +25 -0
- package/dist/src/chats/selection.js +109 -0
- package/dist/src/chats/source.d.ts +23 -0
- package/dist/src/chats/source.js +135 -0
- package/dist/src/chats/structure.d.ts +57 -0
- package/dist/src/chats/structure.js +216 -0
- package/dist/src/chats/workflow-generation-projection.d.ts +8 -0
- package/dist/src/chats/workflow-generation-projection.js +75 -0
- package/dist/src/chats/workflow-generation-source.d.ts +25 -0
- package/dist/src/chats/workflow-generation-source.js +124 -0
- package/dist/src/chats/workflow-generation-text.d.ts +23 -0
- package/dist/src/chats/workflow-generation-text.js +74 -0
- package/dist/src/chats/workflow-generation.d.ts +69 -0
- package/dist/src/chats/workflow-generation.js +142 -0
- package/dist/src/cli/arguments.d.ts +8 -0
- package/dist/src/cli/arguments.js +94 -13
- package/dist/src/cli/client.d.ts +1 -0
- package/dist/src/cli/client.js +38 -1
- package/dist/src/cli/commands.d.ts +3 -0
- package/dist/src/cli/commands.js +29 -11
- package/dist/src/cli/main.d.ts +2 -1
- package/dist/src/cli/main.js +61 -14
- package/dist/src/cli/render.d.ts +3 -0
- package/dist/src/cli/render.js +122 -7
- package/dist/src/cli/usage-render.d.ts +3 -0
- package/dist/src/cli/usage-render.js +28 -0
- package/dist/src/cli/usage.d.ts +3 -2
- package/dist/src/cli/usage.js +42 -7
- package/dist/src/cli/watch-actions.d.ts +10 -0
- package/dist/src/cli/watch-actions.js +58 -0
- package/dist/src/cli/watch-board-view.d.ts +44 -1
- package/dist/src/cli/watch-board-view.js +137 -18
- package/dist/src/cli/watch-board.d.ts +7 -1
- package/dist/src/cli/watch-board.js +48 -11
- package/dist/src/cli/watch-card-client.d.ts +1 -0
- package/dist/src/cli/watch-card-client.js +44 -0
- package/dist/src/cli/watch-card-forms.d.ts +2 -0
- package/dist/src/cli/watch-card-forms.js +33 -0
- package/dist/src/cli/watch-card-reply.d.ts +5 -0
- package/dist/src/cli/watch-card-reply.js +34 -0
- package/dist/src/cli/watch-card-view.d.ts +40 -0
- package/dist/src/cli/watch-card-view.js +86 -0
- package/dist/src/cli/watch-client.d.ts +1 -0
- package/dist/src/cli/watch-client.js +122 -0
- package/dist/src/cli/watch-model.d.ts +10 -4
- package/dist/src/cli/watch-model.js +61 -67
- package/dist/src/cli/watch-page.d.ts +1 -1
- package/dist/src/cli/watch-page.js +89 -86
- package/dist/src/cli/watch-phases.js +3 -3
- package/dist/src/cli/watch-pr-board.d.ts +3 -0
- package/dist/src/cli/watch-pr-board.js +26 -0
- package/dist/src/cli/watch-pr-command.d.ts +18 -0
- package/dist/src/cli/watch-pr-command.js +74 -0
- package/dist/src/cli/watch-pr-identity.d.ts +32 -0
- package/dist/src/cli/watch-pr-identity.js +87 -0
- package/dist/src/cli/watch-pr-observer.d.ts +20 -0
- package/dist/src/cli/watch-pr-observer.js +150 -0
- package/dist/src/cli/watch-pr-settings.d.ts +8 -0
- package/dist/src/cli/watch-pr-settings.js +47 -0
- package/dist/src/cli/watch-pull-request.d.ts +10 -0
- package/dist/src/cli/watch-pull-request.js +130 -0
- package/dist/src/cli/watch-reads.d.ts +7 -1
- package/dist/src/cli/watch-reads.js +44 -18
- package/dist/src/cli/watch-render.d.ts +29 -2
- package/dist/src/cli/watch-render.js +11 -3
- package/dist/src/cli/watch-run-input.d.ts +14 -0
- package/dist/src/cli/watch-run-input.js +49 -0
- package/dist/src/cli/watch-server.d.ts +3 -0
- package/dist/src/cli/watch-server.js +77 -8
- package/dist/src/cli/watch-start-view.d.ts +18 -0
- package/dist/src/cli/watch-start-view.js +77 -0
- package/dist/src/cli/watch-turn.d.ts +1 -0
- package/dist/src/cli/watch-turn.js +8 -3
- package/dist/src/cli/watch-usage.d.ts +6 -0
- package/dist/src/cli/watch-usage.js +65 -0
- package/dist/src/cli/watch-view.d.ts +22 -0
- package/dist/src/cli/watch-view.js +111 -19
- package/dist/src/cli.js +48 -6
- package/dist/src/daemon/analysis-launch.d.ts +5 -0
- package/dist/src/daemon/analysis-launch.js +81 -0
- package/dist/src/daemon/analysis-token.d.ts +6 -0
- package/dist/src/daemon/analysis-token.js +88 -0
- package/dist/src/daemon/analysis-worker.d.ts +20 -0
- package/dist/src/daemon/analysis-worker.js +271 -0
- package/dist/src/daemon/chats-batch-binding.d.ts +23 -0
- package/dist/src/daemon/chats-batch-binding.js +99 -0
- package/dist/src/daemon/chats-capture.d.ts +18 -0
- package/dist/src/daemon/chats-capture.js +194 -0
- package/dist/src/daemon/chats-checkpoints.d.ts +41 -0
- package/dist/src/daemon/chats-checkpoints.js +186 -0
- package/dist/src/daemon/chats-files.d.ts +42 -0
- package/dist/src/daemon/chats-files.js +252 -0
- package/dist/src/daemon/chats-follow-up.d.ts +38 -0
- package/dist/src/daemon/chats-follow-up.js +180 -0
- package/dist/src/daemon/chats-question.d.ts +15 -0
- package/dist/src/daemon/chats-question.js +93 -0
- package/dist/src/daemon/chats-worker.d.ts +13 -0
- package/dist/src/daemon/chats-worker.js +201 -0
- package/dist/src/daemon/claude-turn.d.ts +11 -0
- package/dist/src/daemon/claude-turn.js +96 -15
- package/dist/src/daemon/claude-worker.d.ts +2 -1
- package/dist/src/daemon/claude-worker.js +92 -45
- package/dist/src/daemon/codex-stream.js +16 -0
- package/dist/src/daemon/daemon.d.ts +8 -1
- package/dist/src/daemon/daemon.js +51 -14
- package/dist/src/daemon/delivery-router.js +6 -0
- package/dist/src/daemon/github-calls.d.ts +4 -0
- package/dist/src/daemon/github-calls.js +16 -7
- package/dist/src/daemon/github-ci.d.ts +6 -0
- package/dist/src/daemon/github-ci.js +170 -0
- package/dist/src/daemon/github-merge-call.d.ts +14 -0
- package/dist/src/daemon/github-merge-call.js +73 -0
- package/dist/src/daemon/github-merge.d.ts +3 -5
- package/dist/src/daemon/github-merge.js +52 -95
- package/dist/src/daemon/github-methods.d.ts +28 -0
- package/dist/src/daemon/github-methods.js +84 -16
- package/dist/src/daemon/github-operation.d.ts +2 -0
- package/dist/src/daemon/github-retry.d.ts +6 -0
- package/dist/src/daemon/github-retry.js +49 -0
- package/dist/src/daemon/github-ticket-status.d.ts +3 -0
- package/dist/src/daemon/github-ticket-status.js +294 -0
- package/dist/src/daemon/github-worker.js +2 -1
- package/dist/src/daemon/handoff-worker.d.ts +10 -0
- package/dist/src/daemon/handoff-worker.js +232 -0
- package/dist/src/daemon/issue-queue.d.ts +2 -0
- package/dist/src/daemon/issue-queue.js +186 -43
- package/dist/src/daemon/notify-journal.d.ts +18 -0
- package/dist/src/daemon/notify-journal.js +108 -0
- package/dist/src/daemon/notify-message.js +2 -1
- package/dist/src/daemon/notify.js +176 -37
- package/dist/src/daemon/operation-journal.d.ts +1 -0
- package/dist/src/daemon/operation-journal.js +4 -2
- package/dist/src/daemon/retrying-turn.d.ts +12 -0
- package/dist/src/daemon/retrying-turn.js +231 -0
- package/dist/src/daemon/ticket-dependency-waits.d.ts +18 -0
- package/dist/src/daemon/ticket-dependency-waits.js +60 -0
- package/dist/src/daemon/ticket-registration.d.ts +8 -4
- package/dist/src/daemon/ticket-registration.js +76 -22
- package/dist/src/daemon/ticket-source.d.ts +4 -1
- package/dist/src/daemon/ticket-source.js +8 -2
- package/dist/src/decision-visibility.d.ts +2 -0
- package/dist/src/decision-visibility.js +10 -0
- package/dist/src/execution-pin.d.ts +4 -0
- package/dist/src/execution-pin.js +23 -2
- package/dist/src/host/start-delivery.d.ts +2 -1
- package/dist/src/host/start-delivery.js +1 -1
- package/dist/src/host/step-bridge-host.d.ts +1 -0
- package/dist/src/host/step-bridge-host.js +16 -0
- package/dist/src/host/world-reconciliation.d.ts +3 -0
- package/dist/src/host/world-reconciliation.js +9 -0
- package/dist/src/host/world-runs.js +14 -1
- package/dist/src/issue-identity.d.ts +6 -0
- package/dist/src/issue-identity.js +23 -0
- package/dist/src/service/queue-runs.d.ts +3 -0
- package/dist/src/service/queue-runs.js +39 -0
- package/dist/src/service/registry.d.ts +8 -2
- package/dist/src/service/registry.js +20 -8
- package/dist/src/service/replies.d.ts +2 -0
- package/dist/src/service/run-handlers.js +54 -30
- package/dist/src/service/service.d.ts +3 -0
- package/dist/src/service/service.js +3 -1
- package/dist/src/service/submission-handler.d.ts +1 -0
- package/dist/src/service/submission-handler.js +92 -13
- package/dist/src/service/worker-totals.d.ts +3 -0
- package/dist/src/service/worker-totals.js +52 -0
- package/dist/src/step-bridge.d.ts +9 -0
- package/dist/src/steps/handoff.d.ts +2 -0
- package/dist/src/steps/handoff.js +13 -0
- package/dist/src/ticket-dependencies.d.ts +1 -0
- package/dist/src/ticket-dependencies.js +15 -0
- package/dist/src/ticket-status.d.ts +8 -0
- package/dist/src/ticket-status.js +48 -0
- package/dist/src/token-prices.d.ts +21 -0
- package/dist/src/token-prices.js +108 -0
- package/dist/src/worker-usage.d.ts +31 -0
- package/dist/src/worker-usage.js +44 -0
- package/dist/src/workflow-admission.d.ts +1 -0
- package/dist/src/workflow-candidate.d.ts +29 -0
- package/dist/src/workflow-candidate.js +60 -0
- package/dist/src/workflow-contracts.d.ts +4 -0
- package/dist/src/workflow-contracts.js +32 -5
- package/dist/src/workflow-declaration.d.ts +12 -1
- package/dist/src/workflow-declaration.js +71 -13
- package/dist/src/workflow-discovery.d.ts +18 -0
- package/dist/src/workflow-discovery.js +33 -0
- package/dist/src/workflow-refusals.d.ts +6 -0
- package/dist/src/workflow-refusals.js +39 -0
- package/dist/src/workflows.d.ts +3 -1
- package/dist/src/workflows.js +2 -0
- package/dist/tools/admit-workflow.d.ts +13 -0
- package/dist/tools/admit-workflow.js +94 -0
- package/dist/tools/self-host-processes.js +7 -2
- package/dist/tools/self-host-project.d.ts +17 -0
- package/dist/tools/self-host-project.js +177 -0
- package/dist/tools/self-host-workflows.d.ts +1 -1
- package/dist/tools/self-host-workflows.js +2 -2
- package/dist/tools/self-host.js +30 -84
- package/dist/tools/self-update-host.d.ts +7 -0
- package/dist/tools/self-update-host.js +48 -1
- package/dist/tools/self-update.d.ts +1 -0
- package/dist/tools/self-update.js +85 -13
- package/dist/tools/workflow-bundle-dependencies.d.ts +5 -0
- package/dist/tools/workflow-bundle-dependencies.js +93 -0
- package/dist/tools/workflow-bundles.d.ts +20 -0
- package/dist/tools/workflow-bundles.js +173 -0
- package/dist/tools/workflow-candidate-counts.d.ts +8 -0
- package/dist/tools/workflow-candidate-counts.js +251 -0
- package/dist/tools/workflow-candidate-program.d.ts +11 -0
- package/dist/tools/workflow-candidate-program.js +78 -0
- package/dist/tools/workflow-candidate-source.d.ts +6 -0
- package/dist/tools/workflow-candidate-source.js +107 -0
- package/dist/tools/workflow-candidate-syntax.d.ts +15 -0
- package/dist/tools/workflow-candidate-syntax.js +219 -0
- package/dist/tools/workflow-folder-capture.d.ts +17 -0
- package/dist/tools/workflow-folder-capture.js +216 -0
- package/dist/tools/workflow-folders.d.ts +33 -0
- package/dist/tools/workflow-folders.js +150 -0
- package/dist/tools/workflow-source-effects.d.ts +10 -0
- package/dist/tools/workflow-source-effects.js +213 -0
- package/dist/workflows/analyze-chats.d.ts +15 -0
- package/dist/workflows/analyze-chats.declaration.json +22 -0
- package/dist/workflows/analyze-chats.js +250 -0
- package/dist/workflows/develop-prd.js +71 -3
- package/dist/workflows/develop-ticket.d.ts +1 -7
- package/dist/workflows/develop-ticket.js +47 -141
- package/dist/workflows/issue-labels.d.ts +3 -1
- package/dist/workflows/issue-labels.js +34 -0
- package/dist/workflows/ticket-prompts.d.ts +35 -0
- package/dist/workflows/ticket-prompts.js +143 -0
- package/package.json +25 -5
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import { issueIdentity } from "../issue-identity.js";
|
|
2
|
+
import { linksOf } from "../authority/handoff-chain.js";
|
|
1
3
|
import { runView, unresolvedEffects, } from "../authority/status.js";
|
|
2
4
|
import { record } from "../protocol.js";
|
|
3
5
|
import { committed, failed, invalidRequest, isIdentifier, publicReceipt, rejection, result, wholeNumber, } from "./replies.js";
|
|
6
|
+
import { usageTotals } from "./worker-totals.js";
|
|
4
7
|
import { feedbackOf } from "./decision-handler.js";
|
|
8
|
+
import { queueOverview } from "./submission-handler.js";
|
|
9
|
+
import { queueRunStatuses } from "./queue-runs.js";
|
|
5
10
|
async function snapshot(context) {
|
|
6
11
|
const { authority } = context;
|
|
7
12
|
const state = await authority.state();
|
|
8
13
|
const sequences = await authority.queries.sequences();
|
|
9
14
|
return {
|
|
15
|
+
links: await authority.queries.runLinks(),
|
|
10
16
|
state,
|
|
11
17
|
operations: await authority.operations(),
|
|
12
18
|
sequences: sequences.runs,
|
|
@@ -38,9 +44,7 @@ async function effectInput(context, runId) {
|
|
|
38
44
|
const budget = await effects.budget(runId);
|
|
39
45
|
const stored = await effects.operations(runId);
|
|
40
46
|
return {
|
|
41
|
-
budget: budget
|
|
42
|
-
? null
|
|
43
|
-
: { limit: budget.limit, spent: budget.spent },
|
|
47
|
+
budget: budget ? { limit: budget.limit, spent: budget.spent } : null,
|
|
44
48
|
effects: stored.map((operation) => ({
|
|
45
49
|
operationId: operation.operationId,
|
|
46
50
|
status: operation.status,
|
|
@@ -77,6 +81,8 @@ async function viewOf(context, scope) {
|
|
|
77
81
|
};
|
|
78
82
|
return {
|
|
79
83
|
...runView(input),
|
|
84
|
+
...linksOf({ runId: run.runId, ...scope.snapshot.links[run.runId] }),
|
|
85
|
+
input: await submittedInput(context, details?.submission),
|
|
80
86
|
workflowId: scope.snapshot.workflowIds[run.runId] ?? run.workflow,
|
|
81
87
|
latestAt: times[run.runId] ?? null,
|
|
82
88
|
unresolvedEffects: unresolvedEffects(input),
|
|
@@ -86,6 +92,8 @@ async function viewOf(context, scope) {
|
|
|
86
92
|
function summary(view) {
|
|
87
93
|
return {
|
|
88
94
|
runId: view.runId,
|
|
95
|
+
...linksOf(view),
|
|
96
|
+
input: view.input,
|
|
89
97
|
workflowId: view.workflowId,
|
|
90
98
|
state: view.state,
|
|
91
99
|
condition: view.condition,
|
|
@@ -95,26 +103,6 @@ function summary(view) {
|
|
|
95
103
|
pendingDecisions: view.pendingDecisions,
|
|
96
104
|
};
|
|
97
105
|
}
|
|
98
|
-
function costOf(text) {
|
|
99
|
-
try {
|
|
100
|
-
const cost = record(JSON.parse(text ?? "null"))?.["costUsd"];
|
|
101
|
-
return typeof cost === "number" ? cost : 0;
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
return 0;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
async function claudeCost(context, runId) {
|
|
108
|
-
const { effects, queries } = context.authority;
|
|
109
|
-
const digests = (await effects.operations(runId))
|
|
110
|
-
.filter((operation) => operation.capability === "claude")
|
|
111
|
-
.map((operation) => operation.result)
|
|
112
|
-
.filter((digest) => digest !== null);
|
|
113
|
-
let total = 0;
|
|
114
|
-
for (const digest of digests)
|
|
115
|
-
total += costOf(await queries.document(digest));
|
|
116
|
-
return Math.round(total * 100) / 100;
|
|
117
|
-
}
|
|
118
106
|
function full(view) {
|
|
119
107
|
return {
|
|
120
108
|
...summary(view),
|
|
@@ -123,6 +111,9 @@ function full(view) {
|
|
|
123
111
|
lastConfirmedObservation: view.lastConfirmedObservation,
|
|
124
112
|
recovery: view.recovery,
|
|
125
113
|
claudeCostUsd: view.claudeCostUsd,
|
|
114
|
+
estimatedCostUsd: view.estimatedCostUsd,
|
|
115
|
+
usage: view.usage,
|
|
116
|
+
workerTurns: view.workerTurns,
|
|
126
117
|
budget: view.budget,
|
|
127
118
|
effects: view.effects,
|
|
128
119
|
nextAction: view.nextAction,
|
|
@@ -138,10 +129,18 @@ async function outcomeOf(context, view) {
|
|
|
138
129
|
runId: view.runId,
|
|
139
130
|
atOrBefore: view.latestSequence,
|
|
140
131
|
});
|
|
141
|
-
const
|
|
132
|
+
const payload = event?.payload ?? {};
|
|
133
|
+
const kind = payload["outcome"];
|
|
142
134
|
if (kind === undefined)
|
|
143
135
|
return null;
|
|
144
|
-
|
|
136
|
+
const { ticketStatus, promotionId, outcomeReason, issueUpdate } = payload;
|
|
137
|
+
return {
|
|
138
|
+
kind,
|
|
139
|
+
reason: outcomeReason ?? null,
|
|
140
|
+
...(promotionId === undefined ? {} : { promotionId }),
|
|
141
|
+
...(ticketStatus === undefined ? {} : { ticketStatus }),
|
|
142
|
+
...(issueUpdate === "failed" ? { issueUpdate } : {}),
|
|
143
|
+
};
|
|
145
144
|
}
|
|
146
145
|
function titleOf(input, workflowId) {
|
|
147
146
|
const ticket = input?.["ticket"];
|
|
@@ -169,8 +168,18 @@ async function metadataOf(context, source) {
|
|
|
169
168
|
return {
|
|
170
169
|
ticketId: typeof ticketId === "string" && ticketId !== "" ? ticketId : null,
|
|
171
170
|
title: titleOf(input, source.workflowId),
|
|
171
|
+
...(source.workflowId === "develop-issue"
|
|
172
|
+
? { issueIdentity: issueIdentity(input?.["ticket"]) }
|
|
173
|
+
: {}),
|
|
172
174
|
};
|
|
173
175
|
}
|
|
176
|
+
async function submittedInput(context, digest) {
|
|
177
|
+
const document = digest
|
|
178
|
+
? await context.authority.queries.document(digest)
|
|
179
|
+
: undefined;
|
|
180
|
+
const request = record(JSON.parse(document ?? "null"));
|
|
181
|
+
return record(request?.["submission"]) ?? null;
|
|
182
|
+
}
|
|
174
183
|
async function listed(context, page) {
|
|
175
184
|
const identifiers = Object.keys(page.snapshot.state.runs)
|
|
176
185
|
.sort()
|
|
@@ -181,29 +190,44 @@ async function listed(context, page) {
|
|
|
181
190
|
views.push(await viewOf(context, { snapshot: page.snapshot, runId }));
|
|
182
191
|
const runs = views.filter((view) => view !== undefined).map(summary);
|
|
183
192
|
return result("run-list", {
|
|
193
|
+
...(await queueOverview(context)),
|
|
184
194
|
workflows: context.registry.workflowIds,
|
|
195
|
+
workflowRefusals: context.workflowRefusals ?? [],
|
|
196
|
+
...(Object.keys(context.registry.workflowStages).length === 0
|
|
197
|
+
? {}
|
|
198
|
+
: {
|
|
199
|
+
workflowStages: context.registry.workflowStages,
|
|
200
|
+
}),
|
|
185
201
|
runs,
|
|
186
202
|
next: identifiers.length > shown.length ? (shown.at(-1) ?? null) : null,
|
|
187
203
|
worldReports: context.worldReports?.() ?? [],
|
|
188
204
|
});
|
|
189
205
|
}
|
|
190
|
-
|
|
191
|
-
const { runId } = parameters;
|
|
206
|
+
function listParameters(parameters) {
|
|
192
207
|
const after = parameters["after"] ?? "";
|
|
193
208
|
const limit = parameters["limit"] ?? 50;
|
|
194
209
|
if (typeof after !== "string" || !wholeNumber(limit, 1) || limit > 500)
|
|
210
|
+
return undefined;
|
|
211
|
+
return { after, limit };
|
|
212
|
+
}
|
|
213
|
+
export async function status(context, parameters) {
|
|
214
|
+
if (parameters["runIds"] !== undefined)
|
|
215
|
+
return queueRunStatuses(context, parameters["runIds"]);
|
|
216
|
+
const { runId } = parameters;
|
|
217
|
+
const page = listParameters(parameters);
|
|
218
|
+
if (page === undefined)
|
|
195
219
|
return invalidRequest("The status request is not valid.");
|
|
196
220
|
const current = await snapshot(context);
|
|
197
221
|
if (runId === undefined)
|
|
198
|
-
return listed(context, { snapshot: current,
|
|
222
|
+
return listed(context, { snapshot: current, ...page });
|
|
199
223
|
const named = typeof runId === "string" ? runId : "";
|
|
200
224
|
const view = await viewOf(context, { snapshot: current, runId: named });
|
|
201
225
|
if (view === undefined)
|
|
202
226
|
return failed("unknown-run", { detail: `No run has the ID ${named}.` });
|
|
203
|
-
const
|
|
227
|
+
const totals = await usageTotals(context, view.runId);
|
|
204
228
|
return result("run-status", {
|
|
205
229
|
run: {
|
|
206
|
-
...full({ ...view,
|
|
230
|
+
...full({ ...view, ...totals }),
|
|
207
231
|
outcome: await outcomeOf(context, view),
|
|
208
232
|
...(await metadataOf(context, {
|
|
209
233
|
workflowId: view.workflowId,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Authority, type AuthorityTiming, type Delivery, type EffectSettings, type Refusal } from "../broker/actor/authority.ts";
|
|
2
|
+
import { type WorkflowRefusal } from "../workflow-refusals.ts";
|
|
2
3
|
import { type WorkflowRegistration } from "./registry.ts";
|
|
3
4
|
import { type WorkerPort } from "./worker-port.ts";
|
|
4
5
|
export type { WorkflowRegistration } from "./registry.ts";
|
|
@@ -38,6 +39,8 @@ export interface ServiceOptions {
|
|
|
38
39
|
readonly stateDirectory: string;
|
|
39
40
|
readonly runtimeDirectory: string;
|
|
40
41
|
readonly workflows: readonly WorkflowRegistration[];
|
|
42
|
+
readonly retainedWorkflows?: readonly WorkflowRegistration[];
|
|
43
|
+
readonly workflowRefusals?: readonly WorkflowRefusal[];
|
|
41
44
|
readonly delivery: Delivery;
|
|
42
45
|
readonly host?: (ports: HostPorts) => Promise<ServiceHost>;
|
|
43
46
|
readonly timing?: ServiceTiming;
|
|
@@ -2,6 +2,7 @@ import { lstat, rm } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { authorityTiming, openAuthority, } from "../broker/actor/authority.js";
|
|
4
4
|
import { socketAddress } from "../broker/socket-address.js";
|
|
5
|
+
import { workflowRefusals, } from "../workflow-refusals.js";
|
|
5
6
|
import { changeSignal } from "./changes.js";
|
|
6
7
|
import { decision, questions } from "./decision-handler.js";
|
|
7
8
|
import { events } from "./event-handler.js";
|
|
@@ -87,6 +88,7 @@ async function started(authority, setup) {
|
|
|
87
88
|
handle: dispatcher({
|
|
88
89
|
authority,
|
|
89
90
|
registry,
|
|
91
|
+
workflowRefusals: workflowRefusals(options.workflowRefusals),
|
|
90
92
|
gateway,
|
|
91
93
|
clock,
|
|
92
94
|
changed: changes.next,
|
|
@@ -133,7 +135,7 @@ export async function startService(options) {
|
|
|
133
135
|
if (typeof path !== "string")
|
|
134
136
|
return path;
|
|
135
137
|
const timing = options.timing ?? serviceTiming;
|
|
136
|
-
const registry = workflowRegistry(options.workflows);
|
|
138
|
+
const registry = workflowRegistry(options.workflows, options.retainedWorkflows);
|
|
137
139
|
const opening = await openAuthority(options.stateDirectory, {
|
|
138
140
|
delivery: options.delivery,
|
|
139
141
|
timing: timing.authority,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Fields, type Reply } from "../protocol.ts";
|
|
2
2
|
import { type ServiceContext } from "./replies.ts";
|
|
3
3
|
export declare function runIdentity(commandId: string): string;
|
|
4
|
+
export declare function queueOverview(context: ServiceContext): Promise<Fields>;
|
|
4
5
|
export declare function submissionHandler(context: ServiceContext): (parameters: Fields) => Promise<Reply>;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { handoffChain } from "../authority/handoff-chain.js";
|
|
3
|
+
import { executionCapabilities } from "../execution-pin.js";
|
|
2
4
|
import { setTimeout as delay } from "node:timers/promises";
|
|
3
|
-
import { isIdentifier as isWorkflowIdentifier } from "../authority/commands.js";
|
|
5
|
+
import { isCommandId, isIdentifier as isWorkflowIdentifier, } from "../authority/commands.js";
|
|
4
6
|
import { documentDigest } from "../broker/decision-restore.js";
|
|
5
7
|
import { record } from "../protocol.js";
|
|
6
8
|
import { checkResolution, checkSubmission, normalizedRequest, } from "../submission.js";
|
|
7
|
-
import { committed, failed, invalidRequest,
|
|
9
|
+
import { committed, failed, invalidRequest, publicReceipt, rejection, result, } from "./replies.js";
|
|
8
10
|
export function runIdentity(commandId) {
|
|
9
11
|
return `run-${createHash("sha256").update(commandId).digest("hex").slice(0, 32)}`;
|
|
10
12
|
}
|
|
@@ -49,7 +51,7 @@ function effectBudget(workflow) {
|
|
|
49
51
|
return {};
|
|
50
52
|
return {
|
|
51
53
|
effectLimit: workflow.effectLimit,
|
|
52
|
-
capabilities:
|
|
54
|
+
capabilities: executionCapabilities(workflow.manifest),
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
57
|
function usable(workflow, workflowId) {
|
|
@@ -73,7 +75,13 @@ async function resolved(context, admission) {
|
|
|
73
75
|
const { workflow, workflowId, commandId, submission } = admission;
|
|
74
76
|
const limit = new AbortController();
|
|
75
77
|
const timer = new AbortController();
|
|
76
|
-
const request = {
|
|
78
|
+
const request = {
|
|
79
|
+
workflowId,
|
|
80
|
+
submission,
|
|
81
|
+
commandId,
|
|
82
|
+
signal: limit.signal,
|
|
83
|
+
activeTickets: admission.activeTickets,
|
|
84
|
+
};
|
|
77
85
|
const call = Promise.resolve(request)
|
|
78
86
|
.then((given) => workflow.resolver.resolve(given))
|
|
79
87
|
.then((value) => checkResolution(value) ?? "failure")
|
|
@@ -110,6 +118,9 @@ async function admit(context, admission) {
|
|
|
110
118
|
return unresolved(context, { kind: resolution, commandId });
|
|
111
119
|
if (resolution.kind === "rejected")
|
|
112
120
|
return failed(resolution.code, { commandId, detail: resolution.detail });
|
|
121
|
+
const limited = capacityRefusal(workflow, admission);
|
|
122
|
+
if (limited !== undefined)
|
|
123
|
+
return limited;
|
|
113
124
|
const { input } = resolution;
|
|
114
125
|
if (Buffer.byteLength(input) > context.documentBytes)
|
|
115
126
|
return unresolved(context, { kind: "failure", commandId });
|
|
@@ -124,6 +135,12 @@ async function admit(context, admission) {
|
|
|
124
135
|
input: documentDigest(input),
|
|
125
136
|
resolver: `${workflow.resolver.id}:${workflow.resolver.version}`,
|
|
126
137
|
...effectBudget(workflow),
|
|
138
|
+
...(admission.chain.kind === "handoff"
|
|
139
|
+
? {
|
|
140
|
+
parentRunId: admission.chain.parentRunId,
|
|
141
|
+
chainId: admission.chain.chainId,
|
|
142
|
+
}
|
|
143
|
+
: {}),
|
|
127
144
|
},
|
|
128
145
|
documents: [normalized, input],
|
|
129
146
|
});
|
|
@@ -137,9 +154,32 @@ async function admit(context, admission) {
|
|
|
137
154
|
replayed: receipt.kind === "duplicate",
|
|
138
155
|
});
|
|
139
156
|
}
|
|
157
|
+
function admissionRefusal(workflow, request) {
|
|
158
|
+
const { workflowId, commandId, submission, activeTickets } = request;
|
|
159
|
+
if (workflowId === "develop-issue" &&
|
|
160
|
+
typeof submission["ticket"] === "string" &&
|
|
161
|
+
activeTickets.some((input) => input["ticketId"] === submission["ticket"]))
|
|
162
|
+
return failed("ticket-active", {
|
|
163
|
+
commandId,
|
|
164
|
+
detail: "An active run already works on this ticket.",
|
|
165
|
+
});
|
|
166
|
+
if (workflowId === "develop-issue" && submission["ticket"] !== "next")
|
|
167
|
+
return undefined;
|
|
168
|
+
return capacityRefusal(workflow, request);
|
|
169
|
+
}
|
|
170
|
+
function capacityRefusal(workflow, request) {
|
|
171
|
+
if (workflow.admission !== undefined &&
|
|
172
|
+
request.activeTickets.length >=
|
|
173
|
+
(workflow.admission === "when-idle" ? 1 : workflow.admission))
|
|
174
|
+
return failed("workflow-active", {
|
|
175
|
+
commandId: request.commandId,
|
|
176
|
+
detail: `The workflow ${request.workflowId} already has an active run.`,
|
|
177
|
+
});
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
140
180
|
async function submitted(context, parameters) {
|
|
141
181
|
const { workflowId, commandId } = parameters;
|
|
142
|
-
if (!isWorkflowIdentifier(workflowId) || !
|
|
182
|
+
if (!isWorkflowIdentifier(workflowId) || !isCommandId(commandId))
|
|
143
183
|
return invalidRequest("The workflow ID or the command ID is not valid.");
|
|
144
184
|
const existing = await context.authority.queries.receipt(commandId);
|
|
145
185
|
if (existing !== undefined)
|
|
@@ -147,6 +187,14 @@ async function submitted(context, parameters) {
|
|
|
147
187
|
workflowId,
|
|
148
188
|
submission: parameters["submission"],
|
|
149
189
|
});
|
|
190
|
+
const chain = handoffChain(commandId, commandId.startsWith("handoff:")
|
|
191
|
+
? await context.authority.queries.runOrigins()
|
|
192
|
+
: {});
|
|
193
|
+
if (chain.kind === "refused")
|
|
194
|
+
return failed(chain.reason, {
|
|
195
|
+
commandId,
|
|
196
|
+
detail: "A hand-off chain permits at most five runs and requires a recorded parent.",
|
|
197
|
+
});
|
|
150
198
|
const found = usable(context.registry.byId(workflowId), workflowId);
|
|
151
199
|
if (found.kind === "unusable")
|
|
152
200
|
return found.reply;
|
|
@@ -161,32 +209,63 @@ async function submitted(context, parameters) {
|
|
|
161
209
|
});
|
|
162
210
|
if (check.kind === "rejected")
|
|
163
211
|
return failed(check.code, { commandId, detail: check.detail });
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
212
|
+
const activeTickets = await activeWorkflow(context, workflowId);
|
|
213
|
+
const refusal = admissionRefusal(found.workflow, {
|
|
214
|
+
workflowId,
|
|
215
|
+
commandId,
|
|
216
|
+
submission: check.submission,
|
|
217
|
+
activeTickets,
|
|
218
|
+
});
|
|
219
|
+
if (refusal !== undefined)
|
|
220
|
+
return refusal;
|
|
170
221
|
return admit(context, {
|
|
222
|
+
chain,
|
|
171
223
|
workflow: found.workflow,
|
|
172
224
|
workflowId,
|
|
173
225
|
commandId,
|
|
174
226
|
submission: check.submission,
|
|
175
227
|
normalized: check.normalized,
|
|
228
|
+
activeTickets,
|
|
176
229
|
});
|
|
177
230
|
}
|
|
178
231
|
async function activeWorkflow(context, workflowId) {
|
|
179
232
|
const workflows = await context.authority.queries.workflowIds();
|
|
180
233
|
const state = await context.authority.state();
|
|
181
|
-
|
|
234
|
+
const runs = Object.values(state.runs).filter((run) => workflows[run.runId] === workflowId &&
|
|
182
235
|
!["completed", "failed", "cancelled"].includes(run.status));
|
|
236
|
+
return Promise.all(runs.map(async (run) => {
|
|
237
|
+
const digest = state.details[run.runId]?.input;
|
|
238
|
+
const text = digest
|
|
239
|
+
? await context.authority.queries.document(digest)
|
|
240
|
+
: undefined;
|
|
241
|
+
return record(JSON.parse(text ?? "null")) ?? {};
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
export async function queueOverview(context) {
|
|
245
|
+
const workflow = context.registry.byId("develop-issue");
|
|
246
|
+
if (workflow?.admission === undefined)
|
|
247
|
+
return {};
|
|
248
|
+
const active = await activeWorkflow(context, "develop-issue");
|
|
249
|
+
return {
|
|
250
|
+
queue: {
|
|
251
|
+
lanes: workflow.admission === "when-idle" ? 1 : workflow.admission,
|
|
252
|
+
busy: active.length,
|
|
253
|
+
skipped: [
|
|
254
|
+
...active.flatMap((input) => typeof input["ticketId"] === "string"
|
|
255
|
+
? [{ ticketId: input["ticketId"], reason: "ticket-active" }]
|
|
256
|
+
: []),
|
|
257
|
+
...(workflow.resolver.skippedTickets?.() ?? []),
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
};
|
|
183
261
|
}
|
|
184
262
|
export function submissionHandler(context) {
|
|
185
263
|
const pending = new Map();
|
|
186
264
|
return (parameters) => {
|
|
187
265
|
const keys = [`command:${String(parameters["commandId"])}`];
|
|
188
266
|
const workflowId = String(parameters["workflowId"]);
|
|
189
|
-
if (
|
|
267
|
+
if (workflowId === "develop-issue" ||
|
|
268
|
+
context.registry.byId(workflowId)?.admission !== undefined)
|
|
190
269
|
keys.push(`workflow:${workflowId}`);
|
|
191
270
|
const next = () => submitted(context, parameters);
|
|
192
271
|
const previous = keys
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { record } from "../protocol.js";
|
|
2
|
+
import { priceOf, usageOf, tokenCount, workerTotals, } from "../worker-usage.js";
|
|
3
|
+
function reportFields(text) {
|
|
4
|
+
try {
|
|
5
|
+
return record(JSON.parse(text ?? "null")) ?? {};
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function attemptsOf(value) {
|
|
12
|
+
if (!Array.isArray(value))
|
|
13
|
+
return undefined;
|
|
14
|
+
return value.flatMap((entry) => {
|
|
15
|
+
const fields = record(entry) ?? {};
|
|
16
|
+
const attempt = tokenCount(fields["attempt"]);
|
|
17
|
+
const model = fields["model"];
|
|
18
|
+
if (attempt === null ||
|
|
19
|
+
attempt === 0 ||
|
|
20
|
+
(model !== null && typeof model !== "string"))
|
|
21
|
+
return [];
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
attempt,
|
|
25
|
+
model,
|
|
26
|
+
usage: usageOf(fields["usage"]),
|
|
27
|
+
costUsd: priceOf(fields["costUsd"]),
|
|
28
|
+
estimatedCostUsd: priceOf(fields["estimatedCostUsd"]),
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export async function usageTotals(context, runId) {
|
|
34
|
+
const { effects, queries } = context.authority;
|
|
35
|
+
const operations = (await effects.operations(runId)).filter((operation) => operation.capability === "claude" &&
|
|
36
|
+
["completed", "failed", "stopped", "cancelled", "refused"].includes(operation.status));
|
|
37
|
+
const turns = [];
|
|
38
|
+
for (const operation of operations) {
|
|
39
|
+
const report = reportFields(operation.result === null
|
|
40
|
+
? undefined
|
|
41
|
+
: await queries.document(operation.result));
|
|
42
|
+
const attempts = attemptsOf(report["attempts"]);
|
|
43
|
+
turns.push({
|
|
44
|
+
...(attempts === undefined ? {} : { attempts }),
|
|
45
|
+
operationId: operation.operationId,
|
|
46
|
+
usage: usageOf(report["usage"]),
|
|
47
|
+
costUsd: priceOf(report["costUsd"]),
|
|
48
|
+
estimatedCostUsd: priceOf(report["estimatedCostUsd"]),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return workerTotals(turns);
|
|
52
|
+
}
|
|
@@ -39,6 +39,14 @@ export interface GateRequest {
|
|
|
39
39
|
readonly gateId: string;
|
|
40
40
|
readonly kind: "question" | "approval";
|
|
41
41
|
readonly prompt: string;
|
|
42
|
+
readonly expiresAt?: string;
|
|
43
|
+
}
|
|
44
|
+
export type HandoffValue = string | number | boolean | null | readonly HandoffValue[] | {
|
|
45
|
+
readonly [key: string]: HandoffValue;
|
|
46
|
+
};
|
|
47
|
+
export interface HandoffRequest {
|
|
48
|
+
readonly workflowId: string;
|
|
49
|
+
readonly input: Readonly<Record<string, HandoffValue>>;
|
|
42
50
|
}
|
|
43
51
|
export type GateResult = {
|
|
44
52
|
readonly kind: "opened";
|
|
@@ -53,5 +61,6 @@ export type GateResult = {
|
|
|
53
61
|
export interface StepBridge {
|
|
54
62
|
readonly effect: (request: ProtectedRequest) => Promise<ProtectedResult>;
|
|
55
63
|
readonly gate: (request: GateRequest) => Promise<GateResult>;
|
|
64
|
+
readonly handoff?: (request: HandoffRequest) => Promise<ProtectedResult>;
|
|
56
65
|
}
|
|
57
66
|
export declare function isStepBridge(value: unknown): value is StepBridge;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { isStepBridge, stepBridgeKey, } from "../step-bridge.js";
|
|
2
|
+
export async function handoff(request) {
|
|
3
|
+
"use step";
|
|
4
|
+
const bridge = Reflect.get(globalThis, Symbol.for(stepBridgeKey));
|
|
5
|
+
if (!isStepBridge(bridge) || bridge.handoff === undefined)
|
|
6
|
+
throw new Error("cloving-host-unavailable");
|
|
7
|
+
const result = await bridge.handoff(request);
|
|
8
|
+
if (result.kind !== "completed")
|
|
9
|
+
throw new Error(result.reason ?? result.kind);
|
|
10
|
+
if (result.result === null)
|
|
11
|
+
throw new Error("handoff-result-missing");
|
|
12
|
+
return result.result;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function hardDependencies(ticket: string): readonly string[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function hardDependencies(ticket) {
|
|
2
|
+
const lines = ticket.split(/\r?\n/);
|
|
3
|
+
const start = lines.findIndex((line) => /^## Dependencies\s*$/.test(line));
|
|
4
|
+
if (start === -1)
|
|
5
|
+
return [];
|
|
6
|
+
const following = lines.slice(start + 1);
|
|
7
|
+
const end = following.findIndex((line) => /^#{1,2} /.test(line));
|
|
8
|
+
const section = (end === -1 ? following : following.slice(0, end)).join("\n");
|
|
9
|
+
const sentences = section
|
|
10
|
+
.replace(/\b(?:e\.g|i\.e)\./g, "example")
|
|
11
|
+
.matchAll(/(?:^|[.!?]\s+|\n)\s*(?:[-*+]\s+|\d+\.\s+)?Needs\b([\s\S]*?)(?=[.!?](?:\s|$)|$)/g);
|
|
12
|
+
return [
|
|
13
|
+
...new Set([...sentences].flatMap((sentence) => [...String(sentence[1]).matchAll(/#([1-9]\d*)\b/g)].map((match) => String(match[1])))),
|
|
14
|
+
];
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type TicketStatus = {
|
|
2
|
+
readonly kind: "complete";
|
|
3
|
+
} | {
|
|
4
|
+
readonly kind: "partial";
|
|
5
|
+
readonly remaining: readonly string[];
|
|
6
|
+
};
|
|
7
|
+
export declare function ticketStatus(summary: string, preserved?: unknown): TicketStatus;
|
|
8
|
+
export declare function isTicketStatus(value: unknown): value is "complete" | "partial";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function parsedStatus(summary) {
|
|
2
|
+
const lines = summary.trimEnd().split(/\r?\n/);
|
|
3
|
+
if (lines.filter((line) => /^Ticket status:/i.test(line)).length !== 1)
|
|
4
|
+
return undefined;
|
|
5
|
+
const status = lines.pop();
|
|
6
|
+
if (status === "Ticket status: complete")
|
|
7
|
+
return { kind: "complete" };
|
|
8
|
+
if (status !== "Ticket status: partial")
|
|
9
|
+
return undefined;
|
|
10
|
+
const heading = lines.lastIndexOf("Remaining:");
|
|
11
|
+
const items = heading === -1
|
|
12
|
+
? []
|
|
13
|
+
: lines.slice(heading + 1).filter((line) => line.trim() !== "");
|
|
14
|
+
if (items.length === 0 || items.some((line) => !/^- \S.*$/.test(line)))
|
|
15
|
+
return {
|
|
16
|
+
kind: "partial",
|
|
17
|
+
remaining: ["The worker report has an unreadable Remaining: list."],
|
|
18
|
+
};
|
|
19
|
+
return { kind: "partial", remaining: items.map((line) => line.slice(2)) };
|
|
20
|
+
}
|
|
21
|
+
function preservedStatus(value) {
|
|
22
|
+
if (typeof value !== "object" || value === null)
|
|
23
|
+
return undefined;
|
|
24
|
+
const kind = Reflect.get(value, "kind");
|
|
25
|
+
if (kind === "complete")
|
|
26
|
+
return { kind };
|
|
27
|
+
const remaining = Reflect.get(value, "remaining");
|
|
28
|
+
if (kind !== "partial" ||
|
|
29
|
+
!Array.isArray(remaining) ||
|
|
30
|
+
remaining.length === 0 ||
|
|
31
|
+
remaining.some((item) => typeof item !== "string" || item.trim() === "" || /[\r\n]/.test(item)))
|
|
32
|
+
return undefined;
|
|
33
|
+
return { kind, remaining: remaining.map(String) };
|
|
34
|
+
}
|
|
35
|
+
export function ticketStatus(summary, preserved) {
|
|
36
|
+
const parsed = preserved === undefined
|
|
37
|
+
? parsedStatus(summary)
|
|
38
|
+
: preservedStatus(preserved);
|
|
39
|
+
if (parsed !== undefined)
|
|
40
|
+
return parsed;
|
|
41
|
+
return {
|
|
42
|
+
kind: "partial",
|
|
43
|
+
remaining: ["The worker report has no ticket status line."],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function isTicketStatus(value) {
|
|
47
|
+
return value === "complete" || value === "partial";
|
|
48
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type WorkerUsage } from "./worker-usage.ts";
|
|
2
|
+
export interface TokenPrice {
|
|
3
|
+
readonly input: number;
|
|
4
|
+
readonly cachedInput: number;
|
|
5
|
+
readonly output: number;
|
|
6
|
+
}
|
|
7
|
+
export type TokenPrices = Readonly<Record<string, TokenPrice>>;
|
|
8
|
+
export declare const builtInPriceTable: {
|
|
9
|
+
readonly date: string;
|
|
10
|
+
readonly source: string;
|
|
11
|
+
readonly prices: TokenPrices;
|
|
12
|
+
};
|
|
13
|
+
export declare function parseTokenPrices(value: string | undefined): TokenPrices;
|
|
14
|
+
export declare function parseFallbackModels(value: string | undefined): readonly string[];
|
|
15
|
+
export declare function priceForModel(prices: TokenPrices, model: string | null): TokenPrice | undefined;
|
|
16
|
+
export declare function commandModel(command: readonly string[]): string | null;
|
|
17
|
+
export declare function replaceCommandModel(command: readonly string[], model: string): readonly string[];
|
|
18
|
+
export declare function estimatedCostUsd(usage: WorkerUsage, pricing: {
|
|
19
|
+
readonly worker: "claude" | "codex";
|
|
20
|
+
readonly price: TokenPrice | undefined;
|
|
21
|
+
}): number | null;
|