Skip to content

Commit ab62ba6

Browse files
committed
merge: integrate origin/main into feat/optimization-gitops-flow
Resolves the conflicts on PR #7 that blocked it from merging into main. main shipped `a1e3228 fix(gitops): scope credential walker, harden state and retries` and `eb29042 feat(gitops): add .vapi-ignore for explicit resource opt-out` while this branch was in flight. Most of `a1e3228` undoes regressions this branch introduced — auto-merge handled the non-overlapping changes correctly; two files needed hand resolution. Conflict resolutions -------------------- `.gitignore` — combined: keep `tmp/` alongside main's `.claude/`. `src/pull.ts` - Kept this branch's mtime-based fallback block for the "locally edited, no git" case, with one syntactic cleanup: removed the inner `const folderPath = FOLDER_MAP[resourceType];` shadow since main's `.vapi-ignore` merge introduced an outer `folderPath` at the top of the loop body (line 676) that now covers this scope. - For the deleted-resource block, took main's expanded comment that references `.vapi-ignore` as the mechanism to stop tracking a resource entirely. - For the pull summary, took main's categorized legend (🚫 / ✏️ / 🗑️) — it genuinely surfaces more information than the single "preserved (locally changed)" line — but fixed its `pull:dev:force` example to the org-slug command form `npm run pull -- ${VAPI_ENV} --force`, which is what this branch actually exposes. Additional stale-string fixes made in the same commit ----------------------------------------------------- These are strings that were correct on main before the org-slug refactor but become stale once this branch's renamed scripts land. If left as-is they would actively mis-route users away from the commands this branch defines. All three are single-line text changes: `src/cleanup.ts:108` — `npm run cleanup:${VAPI_ENV} -- --force ...` → `npm run cleanup -- ${VAPI_ENV} --force ...`. `src/cleanup.ts:135` — `npm run pull:${VAPI_ENV}:bootstrap` → `npm run pull -- ${VAPI_ENV} --bootstrap`. `docs/environment-scoped-resources.md` deleted entirely. The whole file documented `push:dev`/`push:stg`/`pull:prod` commands and `resources/dev/` workflows that this branch removed. Also cleaned up the surviving references from `README.md` and `AGENTS.md` project-structure trees. Notable incoming work (auto-merged) ----------------------------------- `src/credentials.ts` — main's scoped credential walker replaces this branch's generic string-walk. Fixes the `provider: openai` enum corruption bug. `src/push.ts` — main's `try { ... } finally { saveState }` wrap around the apply body. Ensures state is flushed on partial failure so retries don't create duplicates. `src/cleanup.ts` — main's `--confirm <slug>` double-gate and empty- state refusal (both previously removed on this branch). `src/pull.ts cleanResource` — main's `null` preservation, so cleared fields like `voicemailMessage: null` don't silently revert on the next push. `src/api.ts` — main's retry loop now covers 5xx in addition to 429. `src/state.ts` — main's atomic state write (tmp + rename) plus loud- throw on JSON parse error. `src/resources.ts` — main sorts directory entries before iteration for deterministic push order across filesystems. `src/config.ts` — main's `loadIgnorePatterns()` / `matchesIgnore()` for the new `.vapi-ignore` feature. `resources/{dev,stg,prod}/.vapi-ignore.example` — new illustrative example files added by main. Harmless as-is; a follow-up commit on the child `fix/p0-gitops-regressions` branch consolidates them to a single canonical `resources/.vapi-ignore.example`. `AGENTS.md`, `docs/learnings/call-duration.md` — doc adds; no conflict. Verification ------------ npm run build → passes (tsc --noEmit, 0 errors) (No test suite yet on this branch — it lives on the stacked child branch `fix/p0-gitops-regressions`.)
2 parents 73e0816 + eb29042 commit ab62ba6

16 files changed

Lines changed: 480 additions & 187 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@ Thumbs.db
1818
# Logs
1919
*.log
2020

21-
tmp/
21+
tmp/
22+
23+
# Local agent state
24+
.claude/

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This project manages **Vapi voice agent configurations** as code. All resources
1010

1111
**Template-safe first run:** In a fresh clone, prefer `npm run pull -- <org> --bootstrap` to refresh `.vapi-state.<org>.json` and credential mappings without materializing the target org's resources into `resources/<org>/`. `npm run push -- <org>` will auto-run the same bootstrap sync when it detects empty or stale state for the resources being applied.
1212

13+
**Excluding resources from sync (`.vapi-ignore`):** To prevent specific resources from being pulled at all (e.g. assistants owned by another team or legacy resources you don't want to manage), create `resources/<env>/.vapi-ignore` with gitignore-style patterns. See `resources/<env>/.vapi-ignore.example` for syntax and examples. Ignored resources are silently skipped on every pull and never tracked in state — distinct from "locally deleted" which keeps an entry in state.
14+
1315
**Learnings & recipes:** Before configuring resources or debugging issues, read the relevant file in **`docs/learnings/`**. Load only what you need:
1416

1517
| Working on | Read |
@@ -58,7 +60,6 @@ This project manages **Vapi voice agent configurations** as code. All resources
5860
```
5961
docs/
6062
├── Vapi Prompt Optimization Guide.md # In-depth prompt authoring
61-
├── environment-scoped-resources.md # Environment isolation & promotion workflow
6263
├── changelog.md # Template for tracking per-customer config changes
6364
└── learnings/ # Gotchas, recipes, and troubleshooting
6465
├── README.md # Task-routed index — start here

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,6 @@ Tracks resource ID ↔ Vapi UUID mappings per org:
497497
vapi-gitops/
498498
├── docs/
499499
│ ├── Vapi Prompt Optimization Guide.md
500-
│ ├── environment-scoped-resources.md
501500
│ └── changelog.md
502501
├── src/
503502
│ ├── setup.ts # Interactive setup wizard

docs/environment-scoped-resources.md

Lines changed: 0 additions & 102 deletions
This file was deleted.

docs/learnings/call-duration.md

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ How to enforce call time limits while ending calls gracefully instead of abruptl
88

99
Vapi provides three layers for managing call duration:
1010

11-
| Mechanism | What it does | Type |
12-
|-----------|-------------|------|
13-
| `maxDurationSeconds` | Hard wall-clock cutoff — call ends immediately when reached | Assistant setting |
14-
| `call.timeElapsed` hook | Fires once at a specific second mark from call start | Hook (deterministic) |
15-
| `endCall` tool | LLM decides to end the call | Tool (probabilistic) |
11+
12+
| Mechanism | What it does | Type |
13+
| ----------------------- | ----------------------------------------------------------- | -------------------- |
14+
| `maxDurationSeconds` | Hard wall-clock cutoff — call ends immediately when reached | Assistant setting |
15+
| `call.timeElapsed` hook | Fires once at a specific second mark from call start | Hook (deterministic) |
16+
| `endCall` tool | LLM decides to end the call | Tool (probabilistic) |
17+
1618

1719
**Key distinction:** Hooks are deterministic (guaranteed to fire). Tools are probabilistic (the LLM chooses when to invoke them). For call discipline, use hooks as the enforcement layer and tools as the courtesy layer.
1820

@@ -138,14 +140,16 @@ hooks:
138140

139141
## Hooks vs Tools vs System Prompt — When to Use Which
140142

141-
| Approach | Reliability | Use when |
142-
|----------|-------------|----------|
143-
| `call.timeElapsed` hook with `say` | Guaranteed | You need a deterministic spoken warning at a fixed time |
144-
| `call.timeElapsed` hook with `message.add` | Guaranteed delivery, LLM interprets | You want the LLM to organically wrap up the conversation |
145-
| `call.timeElapsed` hook with `endCall` tool | Guaranteed | You need a hard graceful end (with goodbye) at a fixed time |
146-
| `maxDurationSeconds` | Guaranteed | Last-resort hard cutoff — no goodbye, call just drops |
147-
| `endCall` tool (LLM-invoked) | Probabilistic | You want the LLM to decide when to end based on conversation context |
148-
| System prompt instruction ("end after 10 min") | Unreliable | Don't rely on this alone — the LLM may not track time accurately |
143+
144+
| Approach | Reliability | Use when |
145+
| ---------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------- |
146+
| `call.timeElapsed` hook with `say` | Guaranteed | You need a deterministic spoken warning at a fixed time |
147+
| `call.timeElapsed` hook with `message.add` | Guaranteed delivery, LLM interprets | You want the LLM to organically wrap up the conversation |
148+
| `call.timeElapsed` hook with `endCall` tool | Guaranteed | You need a hard graceful end (with goodbye) at a fixed time |
149+
| `maxDurationSeconds` | Guaranteed | Last-resort hard cutoff — no goodbye, call just drops |
150+
| `endCall` tool (LLM-invoked) | Probabilistic | You want the LLM to decide when to end based on conversation context |
151+
| System prompt instruction ("end after 10 min") | Unreliable | Don't rely on this alone — the LLM may not track time accurately |
152+
149153

150154
**Best practice:** Layer them. Use hooks for deterministic enforcement and the endCall tool + system prompt for conversational flexibility.
151155

@@ -155,12 +159,14 @@ hooks:
155159

156160
These are **not** the same as call duration limits, but they interact:
157161

158-
| Mechanism | What it does | Default |
159-
|-----------|-------------|---------|
160-
| `silenceTimeoutSeconds` | Ends call after sustained silence | 30s |
161-
| `customer.speech.timeout` hook | Fires action when customer is silent for N seconds | 7.5s, up to 3 times |
162-
| `messagePlan.idleTimeoutSeconds` | Speaks an idle message when conversation stalls | 10s |
163-
| `customerJoinTimeoutSeconds` | Ends call if customer never sends audio | 15s |
162+
163+
| Mechanism | What it does | Default |
164+
| -------------------------------- | -------------------------------------------------- | ------------------- |
165+
| `silenceTimeoutSeconds` | Ends call after sustained silence | 30s |
166+
| `customer.speech.timeout` hook | Fires action when customer is silent for N seconds | 7.5s, up to 3 times |
167+
| `messagePlan.idleTimeoutSeconds` | Speaks an idle message when conversation stalls | 10s |
168+
| `customerJoinTimeoutSeconds` | Ends call if customer never sends audio | 15s |
169+
164170

165171
**`silenceTimeoutSeconds` vs `customer.speech.timeout`:** The timeout **ends the call**. The hook **performs an action** (say, tool, message.add). They are independent — configure them separately. See [assistants.md](assistants.md) for the hook events list.
166172

@@ -184,4 +190,4 @@ The `maxDurationSeconds` timer starts when the first audio chunk is processed. `
184190

185191
### Lambda workers reject long calls
186192

187-
If `maxDurationSeconds >= 15 minutes` (900s), the call is rejected when routed to a Lambda-based worker (reason: `lambda-longcalls-not-accepted`). Long calls require non-Lambda workers.
193+
If `maxDurationSeconds >= 15 minutes` (900s), the call is rejected when routed to a Lambda-based worker (reason: `lambda-longcalls-not-accepted`). Long calls require non-Lambda workers.

resources/dev/.vapi-ignore.example

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# .vapi-ignore — explicit opt-out for resources this repo does NOT manage.
2+
#
3+
# Resources matching any pattern below are skipped during `pull:<env>` —
4+
# never written to disk, never tracked in state. Use this for:
5+
# - Resources owned by another team or repo
6+
# - Legacy/broken resources you don't want to touch
7+
# - Resource types you don't care about (e.g. an entire `simulations/**`)
8+
#
9+
# To activate this file, copy it to `.vapi-ignore` (no `.example` suffix).
10+
#
11+
# ─────────────────────────────────────────────────────────────────────
12+
# Pattern syntax
13+
# ─────────────────────────────────────────────────────────────────────
14+
# Patterns match against `<folderPath>/<resourceId>` (no extension):
15+
# - assistants/...
16+
# - tools/...
17+
# - squads/...
18+
# - structuredOutputs/...
19+
# - simulations/personalities/...
20+
# - simulations/scenarios/...
21+
# - simulations/tests/...
22+
# - simulations/suites/...
23+
#
24+
# Wildcards:
25+
# - `*` matches any characters within a single path segment
26+
# - `**` matches across path segments
27+
# - `?` matches a single character
28+
#
29+
# Lines starting with `#` are comments. Blank lines are ignored.
30+
#
31+
# ─────────────────────────────────────────────────────────────────────
32+
# Examples
33+
# ─────────────────────────────────────────────────────────────────────
34+
35+
# Ignore a specific assistant by exact resource ID
36+
# assistants/ab-assistant-56b80091
37+
38+
# Ignore all assistants whose name starts with `legacy-`
39+
# assistants/legacy-*
40+
41+
# Ignore an entire resource type
42+
# simulations/**
43+
44+
# Ignore tools owned by another team
45+
# tools/team-x-*
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# .vapi-ignore — explicit opt-out for resources this repo does NOT manage.
2+
#
3+
# Resources matching any pattern below are skipped during `pull:<env>` —
4+
# never written to disk, never tracked in state. Use this for:
5+
# - Resources owned by another team or repo
6+
# - Legacy/broken resources you don't want to touch
7+
# - Resource types you don't care about (e.g. an entire `simulations/**`)
8+
#
9+
# To activate this file, copy it to `.vapi-ignore` (no `.example` suffix).
10+
#
11+
# ─────────────────────────────────────────────────────────────────────
12+
# Pattern syntax
13+
# ─────────────────────────────────────────────────────────────────────
14+
# Patterns match against `<folderPath>/<resourceId>` (no extension):
15+
# - assistants/...
16+
# - tools/...
17+
# - squads/...
18+
# - structuredOutputs/...
19+
# - simulations/personalities/...
20+
# - simulations/scenarios/...
21+
# - simulations/tests/...
22+
# - simulations/suites/...
23+
#
24+
# Wildcards:
25+
# - `*` matches any characters within a single path segment
26+
# - `**` matches across path segments
27+
# - `?` matches a single character
28+
#
29+
# Lines starting with `#` are comments. Blank lines are ignored.
30+
#
31+
# ─────────────────────────────────────────────────────────────────────
32+
# Examples
33+
# ─────────────────────────────────────────────────────────────────────
34+
35+
# Ignore a specific assistant by exact resource ID
36+
# assistants/ab-assistant-56b80091
37+
38+
# Ignore all assistants whose name starts with `legacy-`
39+
# assistants/legacy-*
40+
41+
# Ignore an entire resource type
42+
# simulations/**
43+
44+
# Ignore tools owned by another team
45+
# tools/team-x-*

resources/stg/.vapi-ignore.example

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# .vapi-ignore — explicit opt-out for resources this repo does NOT manage.
2+
#
3+
# Resources matching any pattern below are skipped during `pull:<env>` —
4+
# never written to disk, never tracked in state. Use this for:
5+
# - Resources owned by another team or repo
6+
# - Legacy/broken resources you don't want to touch
7+
# - Resource types you don't care about (e.g. an entire `simulations/**`)
8+
#
9+
# To activate this file, copy it to `.vapi-ignore` (no `.example` suffix).
10+
#
11+
# ─────────────────────────────────────────────────────────────────────
12+
# Pattern syntax
13+
# ─────────────────────────────────────────────────────────────────────
14+
# Patterns match against `<folderPath>/<resourceId>` (no extension):
15+
# - assistants/...
16+
# - tools/...
17+
# - squads/...
18+
# - structuredOutputs/...
19+
# - simulations/personalities/...
20+
# - simulations/scenarios/...
21+
# - simulations/tests/...
22+
# - simulations/suites/...
23+
#
24+
# Wildcards:
25+
# - `*` matches any characters within a single path segment
26+
# - `**` matches across path segments
27+
# - `?` matches a single character
28+
#
29+
# Lines starting with `#` are comments. Blank lines are ignored.
30+
#
31+
# ─────────────────────────────────────────────────────────────────────
32+
# Examples
33+
# ─────────────────────────────────────────────────────────────────────
34+
35+
# Ignore a specific assistant by exact resource ID
36+
# assistants/ab-assistant-56b80091
37+
38+
# Ignore all assistants whose name starts with `legacy-`
39+
# assistants/legacy-*
40+
41+
# Ignore an entire resource type
42+
# simulations/**
43+
44+
# Ignore tools owned by another team
45+
# tools/team-x-*

0 commit comments

Comments
 (0)