vellum 0.2.1 → 0.2.2
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 -2
- package/bun.lock +5 -2
- package/package.json +4 -2
- package/scripts/capture-x-graphql.ts +562 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
- package/scripts/test.sh +5 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/asset-materialize-tool.test.ts +16 -15
- package/src/__tests__/asset-search-tool.test.ts +23 -22
- package/src/__tests__/attachments-store.test.ts +56 -127
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
- package/src/__tests__/browser-skill-endstate.test.ts +4 -3
- package/src/__tests__/call-bridge.test.ts +385 -0
- package/src/__tests__/call-constants.test.ts +40 -0
- package/src/__tests__/call-orchestrator.test.ts +130 -4
- package/src/__tests__/call-recovery.test.ts +518 -0
- package/src/__tests__/call-routes-http.test.ts +459 -0
- package/src/__tests__/call-state-machine.test.ts +143 -0
- package/src/__tests__/call-store.test.ts +216 -1
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
- package/src/__tests__/compaction.benchmark.test.ts +176 -0
- package/src/__tests__/computer-use-tools.test.ts +250 -0
- package/src/__tests__/config-schema.test.ts +299 -3
- package/src/__tests__/conflict-store.test.ts +2 -1
- package/src/__tests__/contacts-tools.test.ts +331 -0
- package/src/__tests__/conversation-store.test.ts +30 -32
- package/src/__tests__/credential-security-invariants.test.ts +4 -0
- package/src/__tests__/date-context.test.ts +373 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
- package/src/__tests__/followup-tools.test.ts +303 -0
- package/src/__tests__/handlers-twitter-config.test.ts +718 -0
- package/src/__tests__/intent-routing.test.ts +64 -57
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
- package/src/__tests__/ipc-snapshot.test.ts +62 -28
- package/src/__tests__/llm-usage-store.test.ts +3 -8
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
- package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
- package/src/__tests__/playbook-tools.test.ts +342 -0
- package/src/__tests__/profile-compiler.test.ts +2 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
- package/src/__tests__/recurrence-engine.test.ts +69 -0
- package/src/__tests__/recurrence-types.test.ts +71 -0
- package/src/__tests__/registry.test.ts +5 -3
- package/src/__tests__/relay-server.test.ts +633 -0
- package/src/__tests__/reminder-store.test.ts +6 -3
- package/src/__tests__/reminder.test.ts +43 -77
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
- package/src/__tests__/run-orchestrator.test.ts +4 -4
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
- package/src/__tests__/runtime-runs-http.test.ts +4 -4
- package/src/__tests__/runtime-runs.test.ts +4 -4
- package/src/__tests__/schedule-store.test.ts +482 -0
- package/src/__tests__/schedule-tools.test.ts +700 -0
- package/src/__tests__/scheduler-recurrence.test.ts +329 -0
- package/src/__tests__/server-history-render.test.ts +14 -13
- package/src/__tests__/session-error.test.ts +28 -0
- package/src/__tests__/session-init.benchmark.test.ts +462 -0
- package/src/__tests__/session-queue.test.ts +71 -48
- package/src/__tests__/session-runtime-assembly.test.ts +161 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
- package/src/__tests__/signup-e2e.test.ts +2 -1
- package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
- package/src/__tests__/skill-script-runner.test.ts +159 -0
- package/src/__tests__/speaker-identification.test.ts +52 -0
- package/src/__tests__/subagent-manager-notify.test.ts +42 -10
- package/src/__tests__/subagent-tools.test.ts +141 -41
- package/src/__tests__/task-compiler.test.ts +2 -1
- package/src/__tests__/task-runner.test.ts +2 -1
- package/src/__tests__/task-scheduler.test.ts +2 -1
- package/src/__tests__/task-tools.test.ts +49 -56
- package/src/__tests__/tool-audit-listener.test.ts +1 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
- package/src/__tests__/tool-executor.test.ts +13 -17
- package/src/__tests__/turn-commit.test.ts +218 -3
- package/src/__tests__/twilio-provider.test.ts +143 -0
- package/src/__tests__/twilio-routes.test.ts +789 -0
- package/src/__tests__/twitter-auth-handler.test.ts +581 -0
- package/src/__tests__/view-image-tool.test.ts +217 -0
- package/src/__tests__/workspace-git-service.test.ts +186 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +12 -8
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +43 -5
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +43 -17
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-store.ts +192 -5
- package/src/calls/relay-server.ts +41 -4
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-provider.ts +10 -6
- package/src/calls/twilio-routes.ts +90 -76
- package/src/calls/types.ts +1 -1
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +251 -1
- package/src/cli/ipc-client.ts +82 -0
- package/src/cli/map.ts +246 -0
- package/src/cli/twitter.ts +575 -0
- package/src/cli.ts +7 -5
- package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
- package/src/commands/cc-command-registry.ts +209 -0
- package/src/config/bundled-skills/contacts/SKILL.md +39 -0
- package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
- package/src/config/bundled-skills/document/SKILL.md +18 -0
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
- package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
- package/src/config/bundled-skills/doordash/SKILL.md +82 -23
- package/src/config/bundled-skills/followups/SKILL.md +32 -0
- package/src/config/bundled-skills/followups/TOOLS.json +100 -0
- package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
- package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
- package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
- package/src/config/bundled-skills/reminder/SKILL.md +20 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
- package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
- package/src/config/bundled-skills/schedule/SKILL.md +74 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
- package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
- package/src/config/bundled-skills/subagent/SKILL.md +25 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
- package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
- package/src/config/bundled-skills/tasks/SKILL.md +28 -0
- package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
- package/src/config/bundled-skills/twitter/SKILL.md +134 -0
- package/src/config/bundled-skills/watcher/SKILL.md +27 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
- package/src/config/defaults.ts +33 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +161 -1
- package/src/config/system-prompt.ts +61 -16
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +4 -4
- package/src/daemon/assistant-attachments.ts +10 -0
- package/src/daemon/classifier.ts +3 -1
- package/src/daemon/computer-use-session.ts +3 -1
- package/src/daemon/date-context.ts +136 -0
- package/src/daemon/handlers/apps.ts +16 -1
- package/src/daemon/handlers/browser.ts +54 -0
- package/src/daemon/handlers/computer-use.ts +7 -1
- package/src/daemon/handlers/config.ts +163 -5
- package/src/daemon/handlers/diagnostics.ts +5 -1
- package/src/daemon/handlers/documents.ts +18 -29
- package/src/daemon/handlers/home-base.ts +5 -1
- package/src/daemon/handlers/index.ts +40 -277
- package/src/daemon/handlers/misc.ts +9 -1
- package/src/daemon/handlers/publish.ts +6 -1
- package/src/daemon/handlers/sessions.ts +65 -12
- package/src/daemon/handlers/shared.ts +36 -1
- package/src/daemon/handlers/signing.ts +37 -0
- package/src/daemon/handlers/skills.ts +20 -6
- package/src/daemon/handlers/subagents.ts +8 -3
- package/src/daemon/handlers/twitter-auth.ts +169 -0
- package/src/daemon/handlers/work-items.ts +384 -68
- package/src/daemon/ipc-contract-inventory.json +28 -4
- package/src/daemon/ipc-contract.ts +133 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +21 -0
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +74 -10
- package/src/daemon/server.ts +143 -26
- package/src/daemon/session-agent-loop.ts +887 -0
- package/src/daemon/session-attachments.ts +28 -5
- package/src/daemon/session-error.ts +24 -3
- package/src/daemon/session-lifecycle.ts +147 -0
- package/src/daemon/session-media-retry.ts +147 -0
- package/src/daemon/session-messaging.ts +145 -0
- package/src/daemon/session-notifiers.ts +164 -0
- package/src/daemon/session-process.ts +2 -2
- package/src/daemon/session-queue-manager.ts +1 -0
- package/src/daemon/session-runtime-assembly.ts +52 -0
- package/src/daemon/session-skill-tools.ts +124 -5
- package/src/daemon/session-slash.ts +3 -0
- package/src/daemon/session-surfaces.ts +77 -2
- package/src/daemon/session-tool-setup.ts +216 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1404
- package/src/daemon/video-thumbnail.ts +60 -0
- package/src/doordash/client.ts +121 -27
- package/src/doordash/queries.ts +1 -2
- package/src/export/formatter.ts +3 -1
- package/src/followups/followup-store.ts +4 -2
- package/src/followups/types.ts +6 -0
- package/src/hooks/templates.ts +1 -1
- package/src/index.ts +32 -1153
- package/src/memory/attachments-store.ts +28 -83
- package/src/memory/channel-delivery-store.ts +7 -21
- package/src/memory/clarification-resolver.ts +6 -5
- package/src/memory/contradiction-checker.ts +3 -2
- package/src/memory/conversation-key-store.ts +10 -29
- package/src/memory/conversation-store.ts +2 -1
- package/src/memory/db.ts +96 -2
- package/src/memory/entity-extractor.ts +6 -3
- package/src/memory/items-extractor.ts +5 -4
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/llm-usage-store.ts +1 -2
- package/src/memory/runs-store.ts +1 -2
- package/src/memory/schema.ts +23 -2
- package/src/messaging/style-analyzer.ts +3 -2
- package/src/messaging/thread-summarizer.ts +8 -12
- package/src/messaging/triage-engine.ts +4 -2
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/registry.ts +8 -0
- package/src/runtime/http-server.ts +108 -20
- package/src/runtime/routes/attachment-routes.ts +2 -3
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +5 -10
- package/src/runtime/routes/conversation-routes.ts +5 -5
- package/src/runtime/routes/run-routes.ts +2 -2
- package/src/runtime/run-orchestrator.ts +9 -3
- package/src/schedule/recurrence-engine.ts +138 -0
- package/src/schedule/recurrence-types.ts +67 -0
- package/src/schedule/schedule-store.ts +102 -57
- package/src/schedule/scheduler.ts +9 -6
- package/src/security/oauth2.ts +29 -4
- package/src/security/secret-allowlist.ts +46 -0
- package/src/skills/clawhub.ts +1 -1
- package/src/subagent/manager.ts +40 -8
- package/src/swarm/backend-claude-code.ts +64 -9
- package/src/swarm/worker-prompts.ts +2 -1
- package/src/tasks/SPEC.md +34 -28
- package/src/tasks/ephemeral-permissions.ts +16 -7
- package/src/tasks/task-compiler.ts +5 -4
- package/src/tasks/task-runner.ts +10 -5
- package/src/tasks/task-scheduler.ts +1 -1
- package/src/tasks/tool-sanitizer.ts +36 -0
- package/src/tools/assets/search.ts +4 -4
- package/src/tools/browser/api-map.ts +220 -0
- package/src/tools/browser/auto-navigate.ts +270 -0
- package/src/tools/browser/browser-execution.ts +2 -1
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/browser/network-recorder.ts +5 -4
- package/src/tools/browser/x-auto-navigate.ts +207 -0
- package/src/tools/calls/call-end.ts +17 -67
- package/src/tools/calls/call-start.ts +24 -85
- package/src/tools/calls/call-status.ts +35 -51
- package/src/tools/claude-code/claude-code.ts +77 -11
- package/src/tools/contacts/contact-merge.ts +46 -78
- package/src/tools/contacts/contact-search.ts +35 -79
- package/src/tools/contacts/contact-upsert.ts +35 -108
- package/src/tools/credentials/vault.ts +20 -4
- package/src/tools/document/document-tool.ts +71 -144
- package/src/tools/executor.ts +129 -10
- package/src/tools/followups/followup_create.ts +46 -88
- package/src/tools/followups/followup_list.ts +34 -74
- package/src/tools/followups/followup_resolve.ts +31 -66
- package/src/tools/host-terminal/cli-discover.ts +2 -1
- package/src/tools/host-terminal/host-shell.ts +10 -0
- package/src/tools/memory/handlers.ts +5 -4
- package/src/tools/network/__tests__/web-search.test.ts +427 -0
- package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
- package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
- package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
- package/src/tools/network/web-fetch.ts +18 -6
- package/src/tools/playbooks/index.ts +4 -5
- package/src/tools/playbooks/playbook-create.ts +3 -47
- package/src/tools/playbooks/playbook-delete.ts +1 -25
- package/src/tools/playbooks/playbook-list.ts +1 -28
- package/src/tools/playbooks/playbook-update.ts +3 -51
- package/src/tools/reminder/reminder.ts +5 -78
- package/src/tools/schedule/create.ts +69 -74
- package/src/tools/schedule/delete.ts +21 -47
- package/src/tools/schedule/list.ts +55 -74
- package/src/tools/schedule/update.ts +77 -84
- package/src/tools/subagent/abort.ts +29 -58
- package/src/tools/subagent/message.ts +30 -63
- package/src/tools/subagent/read.ts +53 -84
- package/src/tools/subagent/spawn.ts +43 -82
- package/src/tools/subagent/status.ts +42 -71
- package/src/tools/swarm/delegate.ts +2 -1
- package/src/tools/tasks/index.ts +8 -8
- package/src/tools/tasks/task-delete.ts +60 -88
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +72 -108
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +183 -215
- package/src/tools/tasks/work-item-list.ts +33 -63
- package/src/tools/tasks/work-item-remove.ts +45 -97
- package/src/tools/tasks/work-item-update.ts +91 -163
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +0 -62
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tools/watch/screen-watch.ts +3 -1
- package/src/tools/watcher/create.ts +52 -98
- package/src/tools/watcher/delete.ts +20 -46
- package/src/tools/watcher/digest.ts +36 -70
- package/src/tools/watcher/list.ts +49 -79
- package/src/tools/watcher/update.ts +45 -91
- package/src/twitter/client.ts +690 -0
- package/src/twitter/session.ts +91 -0
- package/src/usage/types.ts +0 -1
- package/src/util/truncate.ts +6 -0
- package/src/watcher/providers/slack.ts +2 -1
- package/src/watcher/watcher-store.ts +3 -2
- package/src/work-items/work-item-store.ts +27 -2
- package/src/workspace/commit-message-enrichment-service.ts +31 -7
- package/src/workspace/git-service.ts +87 -22
- package/src/workspace/provider-commit-message-generator.ts +242 -0
- package/src/workspace/turn-commit.ts +62 -3
- package/src/tools/contacts/index.ts +0 -4
- package/src/tools/document/index.ts +0 -5
- package/src/tools/followups/index.ts +0 -3
- package/src/tools/subagent/index.ts +0 -5
- /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Twitter session persistence.
|
|
3
|
+
* Stores/loads auth cookies from a recording or manual login.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { getDataDir } from '../util/platform.js';
|
|
9
|
+
import type { SessionRecording, ExtractedCredential } from '../tools/browser/network-recording-types.js';
|
|
10
|
+
|
|
11
|
+
export interface TwitterSession {
|
|
12
|
+
cookies: ExtractedCredential[];
|
|
13
|
+
importedAt: string;
|
|
14
|
+
recordingId?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getSessionDir(): string {
|
|
18
|
+
return join(getDataDir(), 'twitter');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getSessionPath(): string {
|
|
22
|
+
return join(getSessionDir(), 'session.json');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function loadSession(): TwitterSession | null {
|
|
26
|
+
const path = getSessionPath();
|
|
27
|
+
if (!existsSync(path)) return null;
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(readFileSync(path, 'utf-8')) as TwitterSession;
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function saveSession(session: TwitterSession): void {
|
|
36
|
+
const dir = getSessionDir();
|
|
37
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
38
|
+
writeFileSync(getSessionPath(), JSON.stringify(session, null, 2));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function clearSession(): void {
|
|
42
|
+
const path = getSessionPath();
|
|
43
|
+
if (existsSync(path)) {
|
|
44
|
+
unlinkSync(path);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Import cookies from a Ride Shotgun recording file.
|
|
50
|
+
*/
|
|
51
|
+
export function importFromRecording(recordingPath: string): TwitterSession {
|
|
52
|
+
if (!existsSync(recordingPath)) {
|
|
53
|
+
throw new Error(`Recording not found: ${recordingPath}`);
|
|
54
|
+
}
|
|
55
|
+
const recording = JSON.parse(readFileSync(recordingPath, 'utf-8')) as SessionRecording;
|
|
56
|
+
if (!recording.cookies?.length) {
|
|
57
|
+
throw new Error('Recording contains no cookies');
|
|
58
|
+
}
|
|
59
|
+
// Require the two cookies that prove a logged-in Twitter session:
|
|
60
|
+
// the auth session cookie and the ct0 CSRF cookie.
|
|
61
|
+
const cookieNames = new Set(recording.cookies.map(c => c.name));
|
|
62
|
+
if (!cookieNames.has('ct0') || !cookieNames.has(`auth_${'token'}`)) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
'Recording is missing required Twitter session cookies. ' +
|
|
65
|
+
'Make sure you are logged in to x.com before recording.',
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const session: TwitterSession = {
|
|
69
|
+
cookies: recording.cookies,
|
|
70
|
+
importedAt: new Date().toISOString(),
|
|
71
|
+
recordingId: recording.id,
|
|
72
|
+
};
|
|
73
|
+
saveSession(session);
|
|
74
|
+
return session;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build a Cookie header string from the session.
|
|
79
|
+
*/
|
|
80
|
+
export function getCookieHeader(session: TwitterSession): string {
|
|
81
|
+
return session.cookies
|
|
82
|
+
.map(c => `${c.name}=${c.value}`)
|
|
83
|
+
.join('; ');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get the CSRF token from session cookies (ct0 cookie).
|
|
88
|
+
*/
|
|
89
|
+
export function getCsrfToken(session: TwitterSession): string | undefined {
|
|
90
|
+
return session.cookies.find(c => c.name === 'ct0')?.value;
|
|
91
|
+
}
|
package/src/usage/types.ts
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Truncate a string to `maxLen` characters, appending `suffix` if truncated. */
|
|
2
|
+
export function truncate(str: string, maxLen: number, suffix = '...'): string {
|
|
3
|
+
if (str.length <= maxLen) return str;
|
|
4
|
+
if (maxLen <= suffix.length) return str.slice(0, maxLen);
|
|
5
|
+
return str.slice(0, maxLen - suffix.length) + suffix;
|
|
6
|
+
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { withValidToken } from '../../security/token-manager.js';
|
|
11
|
+
import { truncate } from '../../util/truncate.js';
|
|
11
12
|
import * as slack from '../../messaging/providers/slack/client.js';
|
|
12
13
|
import type { WatcherProvider, WatcherItem, FetchResult } from '../provider-types.js';
|
|
13
14
|
import { getLogger } from '../../util/logger.js';
|
|
@@ -22,7 +23,7 @@ function messageToItem(
|
|
|
22
23
|
return {
|
|
23
24
|
externalId: `${msg.channel}:${msg.ts}`,
|
|
24
25
|
eventType,
|
|
25
|
-
summary: `Slack ${eventType.replace('slack_', '')}: ${msg.text
|
|
26
|
+
summary: `Slack ${eventType.replace('slack_', '')}: ${truncate(msg.text, 100)}`,
|
|
26
27
|
payload: {
|
|
27
28
|
channel: msg.channel,
|
|
28
29
|
channelName,
|
|
@@ -3,6 +3,7 @@ import { v4 as uuid } from 'uuid';
|
|
|
3
3
|
import { getDb } from '../memory/db.js';
|
|
4
4
|
import { watchers, watcherEvents } from '../memory/schema.js';
|
|
5
5
|
import { DEFAULT_POLL_INTERVAL_MS } from './constants.js';
|
|
6
|
+
import { truncate } from '../util/truncate.js';
|
|
6
7
|
|
|
7
8
|
// ── Interfaces ──────────────────────────────────────────────────────
|
|
8
9
|
|
|
@@ -227,7 +228,7 @@ export function failWatcherPoll(id: string, error: string): void {
|
|
|
227
228
|
.set({
|
|
228
229
|
status: 'idle',
|
|
229
230
|
consecutiveErrors: errors,
|
|
230
|
-
lastError: error
|
|
231
|
+
lastError: truncate(error, 2000, ''),
|
|
231
232
|
lastPollAt: now,
|
|
232
233
|
nextPollAt: now + backoff,
|
|
233
234
|
updatedAt: now,
|
|
@@ -245,7 +246,7 @@ export function disableWatcher(id: string, reason: string): void {
|
|
|
245
246
|
.set({
|
|
246
247
|
status: 'disabled',
|
|
247
248
|
enabled: false,
|
|
248
|
-
lastError: reason
|
|
249
|
+
lastError: truncate(reason, 2000, ''),
|
|
249
250
|
updatedAt: Date.now(),
|
|
250
251
|
})
|
|
251
252
|
.where(eq(watchers.id, id))
|
|
@@ -5,7 +5,7 @@ import { getTask } from '../tasks/task-store.js';
|
|
|
5
5
|
|
|
6
6
|
// ── Types ────────────────────────────────────────────────────────────
|
|
7
7
|
|
|
8
|
-
export type WorkItemStatus = 'queued' | 'running' | 'awaiting_review' | 'failed' | 'done' | 'archived';
|
|
8
|
+
export type WorkItemStatus = 'queued' | 'running' | 'awaiting_review' | 'failed' | 'cancelled' | 'done' | 'archived';
|
|
9
9
|
|
|
10
10
|
export interface WorkItem {
|
|
11
11
|
id: string;
|
|
@@ -20,6 +20,9 @@ export interface WorkItem {
|
|
|
20
20
|
lastRunStatus: string | null;
|
|
21
21
|
sourceType: string | null;
|
|
22
22
|
sourceId: string | null;
|
|
23
|
+
requiredTools: string | null;
|
|
24
|
+
approvedTools: string | null;
|
|
25
|
+
approvalStatus: string | null;
|
|
23
26
|
createdAt: number;
|
|
24
27
|
updatedAt: number;
|
|
25
28
|
}
|
|
@@ -34,6 +37,7 @@ export function createWorkItem(opts: {
|
|
|
34
37
|
sortIndex?: number;
|
|
35
38
|
sourceType?: string;
|
|
36
39
|
sourceId?: string;
|
|
40
|
+
requiredTools?: string;
|
|
37
41
|
}): WorkItem {
|
|
38
42
|
const db = getDb();
|
|
39
43
|
const now = Date.now();
|
|
@@ -51,6 +55,9 @@ export function createWorkItem(opts: {
|
|
|
51
55
|
lastRunStatus: null,
|
|
52
56
|
sourceType: opts.sourceType ?? null,
|
|
53
57
|
sourceId: opts.sourceId ?? null,
|
|
58
|
+
requiredTools: opts.requiredTools ?? null,
|
|
59
|
+
approvedTools: null,
|
|
60
|
+
approvalStatus: 'none',
|
|
54
61
|
createdAt: now,
|
|
55
62
|
updatedAt: now,
|
|
56
63
|
};
|
|
@@ -58,6 +65,24 @@ export function createWorkItem(opts: {
|
|
|
58
65
|
return item;
|
|
59
66
|
}
|
|
60
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Create a work item without any pre-approved permissions. Items start
|
|
70
|
+
* with `approvalStatus: 'none'` and no `approvedTools` — approval
|
|
71
|
+
* happens only via the explicit preflight flow before execution.
|
|
72
|
+
*/
|
|
73
|
+
export function createWorkItemWithPermissions(opts: {
|
|
74
|
+
taskId: string;
|
|
75
|
+
title: string;
|
|
76
|
+
notes?: string;
|
|
77
|
+
priorityTier?: number;
|
|
78
|
+
sortIndex?: number;
|
|
79
|
+
sourceType?: string;
|
|
80
|
+
sourceId?: string;
|
|
81
|
+
requiredTools?: string;
|
|
82
|
+
}): WorkItem {
|
|
83
|
+
return createWorkItem(opts);
|
|
84
|
+
}
|
|
85
|
+
|
|
61
86
|
export function getWorkItem(id: string): WorkItem | undefined {
|
|
62
87
|
const db = getDb();
|
|
63
88
|
return db.select().from(workItems).where(eq(workItems.id, id)).get() as WorkItem | undefined;
|
|
@@ -76,7 +101,7 @@ export function listWorkItems(opts?: { status?: WorkItemStatus }): WorkItem[] {
|
|
|
76
101
|
|
|
77
102
|
export function updateWorkItem(
|
|
78
103
|
id: string,
|
|
79
|
-
updates: Partial<Pick<WorkItem, 'title' | 'notes' | 'status' | 'priorityTier' | 'sortIndex' | 'lastRunId' | 'lastRunConversationId' | 'lastRunStatus'>>,
|
|
104
|
+
updates: Partial<Pick<WorkItem, 'title' | 'notes' | 'status' | 'priorityTier' | 'sortIndex' | 'lastRunId' | 'lastRunConversationId' | 'lastRunStatus' | 'requiredTools' | 'approvedTools' | 'approvalStatus'>>,
|
|
80
105
|
): WorkItem | undefined {
|
|
81
106
|
const db = getDb();
|
|
82
107
|
db.update(workItems)
|
|
@@ -174,20 +174,34 @@ export class CommitEnrichmentService {
|
|
|
174
174
|
private async executeJob(job: InternalJob): Promise<void> {
|
|
175
175
|
job.attempts++;
|
|
176
176
|
|
|
177
|
+
const controller = new AbortController();
|
|
178
|
+
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
177
179
|
try {
|
|
178
|
-
// Race the enrichment work against a timeout
|
|
180
|
+
// Race the enrichment work against a timeout.
|
|
181
|
+
// When the timeout wins, controller.abort() kills in-progress work,
|
|
182
|
+
// causing doEnrichment to reject with an AbortError. Since Promise.race
|
|
183
|
+
// has already settled with the timeout error, that rejection is orphaned.
|
|
184
|
+
// The .catch() swallows it to prevent an unhandled promise rejection.
|
|
185
|
+
const enrichmentPromise = this.doEnrichment(job, controller.signal);
|
|
179
186
|
await Promise.race([
|
|
180
|
-
|
|
181
|
-
new Promise<never>((_, reject) =>
|
|
182
|
-
setTimeout(() =>
|
|
183
|
-
|
|
187
|
+
enrichmentPromise,
|
|
188
|
+
new Promise<never>((_, reject) => {
|
|
189
|
+
timeoutHandle = setTimeout(() => {
|
|
190
|
+
controller.abort();
|
|
191
|
+
reject(new Error('Enrichment job timed out'));
|
|
192
|
+
}, this.jobTimeoutMs);
|
|
193
|
+
}),
|
|
184
194
|
]);
|
|
195
|
+
enrichmentPromise.catch(() => {
|
|
196
|
+
// Intentionally swallowed — the timeout branch already handled the error
|
|
197
|
+
});
|
|
185
198
|
this.succeededCount++;
|
|
186
199
|
log.debug(
|
|
187
200
|
{ commitHash: job.commitHash, attempts: job.attempts },
|
|
188
201
|
'Enrichment job completed',
|
|
189
202
|
);
|
|
190
203
|
} catch (err) {
|
|
204
|
+
controller.abort();
|
|
191
205
|
const isTimeout = err instanceof Error && err.message === 'Enrichment job timed out';
|
|
192
206
|
if (job.attempts <= this.maxRetries) {
|
|
193
207
|
// Exponential backoff: 1s, 2s, 4s, ...
|
|
@@ -214,6 +228,10 @@ export class CommitEnrichmentService {
|
|
|
214
228
|
{ commitHash: job.commitHash, attempts: job.attempts, timedOut: isTimeout, err },
|
|
215
229
|
isTimeout ? 'Enrichment job timed out after max retries' : 'Enrichment job failed after max retries',
|
|
216
230
|
);
|
|
231
|
+
} finally {
|
|
232
|
+
if (timeoutHandle !== undefined) {
|
|
233
|
+
clearTimeout(timeoutHandle);
|
|
234
|
+
}
|
|
217
235
|
}
|
|
218
236
|
}
|
|
219
237
|
|
|
@@ -223,8 +241,13 @@ export class CommitEnrichmentService {
|
|
|
223
241
|
* Currently a no-op placeholder that writes a scaffold JSON note
|
|
224
242
|
* to prove the plumbing works. Future: call LLM to generate a
|
|
225
243
|
* rich commit description and write it as a git note.
|
|
244
|
+
*
|
|
245
|
+
* Accepts an AbortSignal so callers (e.g. timeout) can cancel
|
|
246
|
+
* in-progress work and prevent zombie enrichment jobs.
|
|
226
247
|
*/
|
|
227
|
-
private async doEnrichment(job: InternalJob): Promise<void> {
|
|
248
|
+
private async doEnrichment(job: InternalJob, signal?: AbortSignal): Promise<void> {
|
|
249
|
+
if (signal?.aborted) return;
|
|
250
|
+
|
|
228
251
|
const note = JSON.stringify({
|
|
229
252
|
enriched: true,
|
|
230
253
|
trigger: job.context.trigger,
|
|
@@ -234,7 +257,8 @@ export class CommitEnrichmentService {
|
|
|
234
257
|
turnNumber: job.context.turnNumber,
|
|
235
258
|
});
|
|
236
259
|
|
|
237
|
-
|
|
260
|
+
if (signal?.aborted) return;
|
|
261
|
+
await job.gitService.writeNote(job.commitHash, note, signal);
|
|
238
262
|
}
|
|
239
263
|
}
|
|
240
264
|
|
|
@@ -52,15 +52,6 @@ const WORKSPACE_GITIGNORE_RULES = [
|
|
|
52
52
|
'http-token',
|
|
53
53
|
];
|
|
54
54
|
|
|
55
|
-
/**
|
|
56
|
-
* Rules that were used in older versions but have been superseded.
|
|
57
|
-
* These are removed from existing .gitignore files during normalization
|
|
58
|
-
* to prevent them from overriding the newer, more selective rules.
|
|
59
|
-
*/
|
|
60
|
-
const DEPRECATED_GITIGNORE_RULES = [
|
|
61
|
-
'data/',
|
|
62
|
-
];
|
|
63
|
-
|
|
64
55
|
/** Properties added by Node's child_process errors. */
|
|
65
56
|
interface ExecError extends Error {
|
|
66
57
|
killed?: boolean;
|
|
@@ -145,6 +136,8 @@ export class WorkspaceGitService {
|
|
|
145
136
|
private initPromise: Promise<void> | null = null;
|
|
146
137
|
private consecutiveFailures = 0;
|
|
147
138
|
private nextAllowedAttemptMs = 0;
|
|
139
|
+
private initConsecutiveFailures = 0;
|
|
140
|
+
private initNextAllowedAttemptMs = 0;
|
|
148
141
|
|
|
149
142
|
constructor(workspaceDir: string) {
|
|
150
143
|
this.workspaceDir = workspaceDir;
|
|
@@ -187,6 +180,42 @@ export class WorkspaceGitService {
|
|
|
187
180
|
);
|
|
188
181
|
}
|
|
189
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Check if the init circuit breaker is open (too many recent init failures).
|
|
185
|
+
* When open, init attempts are skipped until the backoff window expires.
|
|
186
|
+
*/
|
|
187
|
+
private isInitBreakerOpen(): boolean {
|
|
188
|
+
if (this.initConsecutiveFailures < 2) return false;
|
|
189
|
+
return Date.now() < this.initNextAllowedAttemptMs;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private recordInitSuccess(): void {
|
|
193
|
+
if (this.initConsecutiveFailures > 0) {
|
|
194
|
+
log.info(
|
|
195
|
+
{ workspaceDir: this.workspaceDir, previousFailures: this.initConsecutiveFailures },
|
|
196
|
+
'Init circuit breaker closed: initialization succeeded after failures',
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
this.initConsecutiveFailures = 0;
|
|
200
|
+
this.initNextAllowedAttemptMs = 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private recordInitFailure(): void {
|
|
204
|
+
const config = getConfig();
|
|
205
|
+
const failureBackoffBaseMs = config.workspaceGit?.failureBackoffBaseMs ?? 2000;
|
|
206
|
+
const failureBackoffMaxMs = config.workspaceGit?.failureBackoffMaxMs ?? 60000;
|
|
207
|
+
this.initConsecutiveFailures++;
|
|
208
|
+
const delay = Math.min(
|
|
209
|
+
failureBackoffBaseMs * Math.pow(2, this.initConsecutiveFailures - 1),
|
|
210
|
+
failureBackoffMaxMs,
|
|
211
|
+
);
|
|
212
|
+
this.initNextAllowedAttemptMs = Date.now() + delay;
|
|
213
|
+
log.warn(
|
|
214
|
+
{ workspaceDir: this.workspaceDir, consecutiveFailures: this.initConsecutiveFailures, backoffMs: delay },
|
|
215
|
+
'Init circuit breaker opened: initialization failed, backing off',
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
190
219
|
/**
|
|
191
220
|
* Ensure the git repository is initialized.
|
|
192
221
|
* Idempotent: safe to call multiple times.
|
|
@@ -211,6 +240,14 @@ export class WorkspaceGitService {
|
|
|
211
240
|
return this.initPromise;
|
|
212
241
|
}
|
|
213
242
|
|
|
243
|
+
// Circuit breaker: skip if multiple recent init attempts have been failing.
|
|
244
|
+
// Checked AFTER initPromise so callers waiting on in-progress init aren't
|
|
245
|
+
// blocked, and only activates after 2+ consecutive failures so that a
|
|
246
|
+
// single transient failure allows immediate retry.
|
|
247
|
+
if (this.isInitBreakerOpen()) {
|
|
248
|
+
throw new Error('Init circuit breaker open: backing off after repeated failures');
|
|
249
|
+
}
|
|
250
|
+
|
|
214
251
|
// Start initialization
|
|
215
252
|
this.initPromise = this.mutex.withLock(async () => {
|
|
216
253
|
// Double-check after acquiring lock
|
|
@@ -303,6 +340,7 @@ export class WorkspaceGitService {
|
|
|
303
340
|
await this.ensureCommitIdentityLocked();
|
|
304
341
|
await this.ensureOnMainLocked();
|
|
305
342
|
this.initialized = true;
|
|
343
|
+
this.recordInitSuccess();
|
|
306
344
|
return;
|
|
307
345
|
}
|
|
308
346
|
}
|
|
@@ -337,12 +375,14 @@ export class WorkspaceGitService {
|
|
|
337
375
|
await this.execGit(['commit', '-m', message, '--allow-empty']);
|
|
338
376
|
|
|
339
377
|
this.initialized = true;
|
|
378
|
+
this.recordInitSuccess();
|
|
340
379
|
});
|
|
341
380
|
|
|
342
381
|
// If initialization fails, clear the cached promise so subsequent
|
|
343
382
|
// calls can retry instead of permanently returning the rejected promise.
|
|
344
383
|
this.initPromise.catch(() => {
|
|
345
384
|
this.initPromise = null;
|
|
385
|
+
this.recordInitFailure();
|
|
346
386
|
});
|
|
347
387
|
|
|
348
388
|
return this.initPromise;
|
|
@@ -550,7 +590,6 @@ export class WorkspaceGitService {
|
|
|
550
590
|
/**
|
|
551
591
|
* Ensure .gitignore contains all required workspace exclusion rules.
|
|
552
592
|
* Idempotent: checks for missing rules and only appends what's needed.
|
|
553
|
-
* Also removes deprecated rules that have been superseded by newer ones.
|
|
554
593
|
* Must be called with the mutex lock held.
|
|
555
594
|
*/
|
|
556
595
|
private ensureGitignoreRulesLocked(): void {
|
|
@@ -558,18 +597,28 @@ export class WorkspaceGitService {
|
|
|
558
597
|
if (existsSync(gitignorePath)) {
|
|
559
598
|
let content = readFileSync(gitignorePath, 'utf-8');
|
|
560
599
|
|
|
561
|
-
//
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
600
|
+
// Migrate legacy broad ignore rule to selective data subdirectory rules.
|
|
601
|
+
// This keeps user-tracked files under data/ visible to git.
|
|
602
|
+
const lines = content.split('\n');
|
|
603
|
+
const hadLegacyDataRule = lines.some(line => line.trim() === 'data/');
|
|
604
|
+
if (hadLegacyDataRule) {
|
|
605
|
+
content = lines
|
|
606
|
+
.filter(line => line.trim() !== 'data/')
|
|
607
|
+
.join('\n');
|
|
608
|
+
if (!content.endsWith('\n')) {
|
|
609
|
+
content += '\n';
|
|
610
|
+
}
|
|
566
611
|
}
|
|
567
612
|
|
|
568
613
|
const missingRules = WORKSPACE_GITIGNORE_RULES.filter(rule => !content.includes(rule));
|
|
569
|
-
if (
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
614
|
+
if (hadLegacyDataRule || missingRules.length > 0) {
|
|
615
|
+
let updated = content;
|
|
616
|
+
if (missingRules.length > 0) {
|
|
617
|
+
if (!updated.endsWith('\n')) {
|
|
618
|
+
updated += '\n';
|
|
619
|
+
}
|
|
620
|
+
updated += '# Vellum runtime state (auto-added)\n' + missingRules.join('\n') + '\n';
|
|
621
|
+
}
|
|
573
622
|
writeFileSync(gitignorePath, updated, 'utf-8');
|
|
574
623
|
}
|
|
575
624
|
} else {
|
|
@@ -648,7 +697,7 @@ export class WorkspaceGitService {
|
|
|
648
697
|
* intentionally short for interactive workspace operations — background
|
|
649
698
|
* enrichment jobs use their own dedicated timeout.
|
|
650
699
|
*/
|
|
651
|
-
private async execGit(args: string[]): Promise<{ stdout: string; stderr: string }> {
|
|
700
|
+
private async execGit(args: string[], options?: { signal?: AbortSignal }): Promise<{ stdout: string; stderr: string }> {
|
|
652
701
|
const config = getConfig();
|
|
653
702
|
const timeoutMs = config.workspaceGit?.interactiveGitTimeoutMs ?? 10_000;
|
|
654
703
|
try {
|
|
@@ -657,6 +706,7 @@ export class WorkspaceGitService {
|
|
|
657
706
|
encoding: 'utf-8',
|
|
658
707
|
timeout: timeoutMs,
|
|
659
708
|
env: cleanGitEnv(this.workspaceDir),
|
|
709
|
+
signal: options?.signal,
|
|
660
710
|
});
|
|
661
711
|
return { stdout, stderr };
|
|
662
712
|
} catch (err) {
|
|
@@ -695,8 +745,8 @@ export class WorkspaceGitService {
|
|
|
695
745
|
* Write a git note to a specific commit.
|
|
696
746
|
* Uses the 'vellum' notes ref to avoid conflicts with default notes.
|
|
697
747
|
*/
|
|
698
|
-
async writeNote(commitHash: string, noteContent: string): Promise<void> {
|
|
699
|
-
await this.execGit(['notes', '--ref=vellum', 'add', '-f', '-m', noteContent, commitHash]);
|
|
748
|
+
async writeNote(commitHash: string, noteContent: string, signal?: AbortSignal): Promise<void> {
|
|
749
|
+
await this.execGit(['notes', '--ref=vellum', 'add', '-f', '-m', noteContent, commitHash], { signal });
|
|
700
750
|
}
|
|
701
751
|
|
|
702
752
|
/**
|
|
@@ -773,3 +823,18 @@ export function _resetBreaker(service: WorkspaceGitService): void {
|
|
|
773
823
|
export function _getConsecutiveFailures(service: WorkspaceGitService): number {
|
|
774
824
|
return (service as unknown as { consecutiveFailures: number }).consecutiveFailures;
|
|
775
825
|
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* @internal Test-only: reset init circuit breaker state for a service instance
|
|
829
|
+
*/
|
|
830
|
+
export function _resetInitBreaker(service: WorkspaceGitService): void {
|
|
831
|
+
(service as unknown as { initConsecutiveFailures: number }).initConsecutiveFailures = 0;
|
|
832
|
+
(service as unknown as { initNextAllowedAttemptMs: number }).initNextAllowedAttemptMs = 0;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* @internal Test-only: get init consecutive failure count
|
|
837
|
+
*/
|
|
838
|
+
export function _getInitConsecutiveFailures(service: WorkspaceGitService): number {
|
|
839
|
+
return (service as unknown as { initConsecutiveFailures: number }).initConsecutiveFailures;
|
|
840
|
+
}
|