@stackbone/cli 0.7.0 → 0.8.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.
@@ -0,0 +1,16 @@
1
+ -- STA3-33: Studio and the box address a person by this opaque per-agent id
2
+ -- instead of the phone or email in user_id, which leaked into URLs and logs.
3
+ -- It lives on the revision row because an erase bumps the revision and keeps
4
+ -- the row, so a person who is deleted and comes back keeps the same id.
5
+
6
+ ALTER TABLE stackbone_platform.memory_user_revisions
7
+ ADD COLUMN person_id uuid NOT NULL DEFAULT gen_random_uuid(),
8
+ ADD CONSTRAINT memory_user_revisions_person_id_key UNIQUE (person_id);
9
+
10
+ -- A person with facts or calls but no revision row yet has no id to open.
11
+ INSERT INTO stackbone_platform.memory_user_revisions (agent_id, user_id, revision)
12
+ SELECT agent_id, user_id, 0 FROM (
13
+ SELECT agent_id, user_id FROM stackbone_platform.memory_facts
14
+ UNION SELECT agent_id, user_id FROM stackbone_platform.memory_calls
15
+ ) AS people
16
+ ON CONFLICT (agent_id, user_id) DO NOTHING;
@@ -0,0 +1,31 @@
1
+ -- STA3-35: an erase keeps the revision row as a tombstone, so an add that
2
+ -- snapshotted before the erase still fails its revision check and cannot write
3
+ -- the erased memory back. The tombstone kept the phone or email in user_id; it
4
+ -- now keeps only the opaque person_id, so user_id is nullable and person_id is
5
+ -- the key. UNIQUE (agent_id, user_id) stays the lookup the SDK's
6
+ -- ON CONFLICT (agent_id, user_id) infers, and Postgres treats NULLs as
7
+ -- distinct, so an agent can hold many erased rows.
8
+
9
+ ALTER TABLE stackbone_platform.memory_user_revisions
10
+ DROP CONSTRAINT memory_user_revisions_pkey;
11
+ ALTER TABLE stackbone_platform.memory_user_revisions
12
+ ALTER COLUMN user_id DROP NOT NULL,
13
+ ADD CONSTRAINT memory_user_revisions_pkey PRIMARY KEY (person_id),
14
+ DROP CONSTRAINT memory_user_revisions_person_id_key,
15
+ ADD CONSTRAINT memory_user_revisions_agent_id_user_id_key UNIQUE (agent_id, user_id);
16
+
17
+ -- People erased before this change. Only an erase deletes memory_calls, and
18
+ -- every committed add writes a call with its revision bump, so a row with no
19
+ -- facts and no calls on its own agent is an erased tombstone. The rule holds
20
+ -- only here: once adds claim the row at snapshot, a failed add can leave such
21
+ -- a row for a live person.
22
+ UPDATE stackbone_platform.memory_user_revisions AS revisions
23
+ SET user_id = NULL
24
+ WHERE NOT EXISTS (
25
+ SELECT 1 FROM stackbone_platform.memory_facts AS facts
26
+ WHERE facts.agent_id = revisions.agent_id AND facts.user_id = revisions.user_id
27
+ )
28
+ AND NOT EXISTS (
29
+ SELECT 1 FROM stackbone_platform.memory_calls AS calls
30
+ WHERE calls.agent_id = revisions.agent_id AND calls.user_id = revisions.user_id
31
+ );
@@ -0,0 +1,14 @@
1
+ -- STA3-57: lexical + hybrid search, opt-in per collection.
2
+ ALTER TABLE stackbone_platform.rag_collections
3
+ ADD COLUMN IF NOT EXISTS hybrid_enabled boolean NOT NULL DEFAULT false,
4
+ ADD COLUMN IF NOT EXISTS number_format text NOT NULL DEFAULT 'eu'
5
+ CONSTRAINT rag_collections_number_format_check CHECK (number_format IN ('eu', 'us'));
6
+
7
+ -- Nullable: ingest writes the terms only while the collection has hybrid on
8
+ -- (STA3-57 D11). The terms are normalised in TypeScript, so no `unaccent` (D6).
9
+ ALTER TABLE stackbone_platform.rag_chunks
10
+ ADD COLUMN IF NOT EXISTS lexical tsvector;
11
+
12
+ -- Built under the migrator's advisory lock, like the 1536 index in 0103.
13
+ CREATE INDEX IF NOT EXISTS rag_chunks_lexical_gin
14
+ ON stackbone_platform.rag_chunks USING gin (lexical);
@@ -0,0 +1,3 @@
1
+ ALTER TABLE stackbone_platform.rag_collections
2
+ ADD COLUMN IF NOT EXISTS extract_enabled boolean NOT NULL DEFAULT false,
3
+ ADD COLUMN IF NOT EXISTS extract_fields jsonb NOT NULL DEFAULT '[]';
@@ -0,0 +1,29 @@
1
+ -- 0118_voice_outbound_calls.sql — the answered outbound calls a workflow placed
2
+ -- (`placeCall`, STA3-56) whose `onEnd` workflow has not started yet.
3
+ --
4
+ -- A row is written when the callee answers and deleted once `onEnd` has started,
5
+ -- so the table holds only live calls and the phone number never outlives the
6
+ -- call. It lives in Postgres, not in the box's memory, because the fallback that
7
+ -- starts `onEnd` for a worker that never posts its completion must also cover a
8
+ -- box that restarted mid-call.
9
+ --
10
+ -- `claimed_at` is the once-only guard: whoever sets it from NULL (the worker's
11
+ -- `/complete` or the sweep) starts `onEnd`. A claim older than the sweep's
12
+ -- reclaim window belongs to a box that died between the claim and the start, and
13
+ -- may be taken again. `room_gone_at` is when the sweep first saw the LiveKit room
14
+ -- gone; the sweep gives the worker a grace period after it before it ends the
15
+ -- call as `failed`.
16
+ --
17
+ -- `context` is what the workflow passed to `placeCall`, opaque to the box and
18
+ -- never logged.
19
+ CREATE TABLE IF NOT EXISTS stackbone_platform.voice_outbound_calls (
20
+ session_key text PRIMARY KEY,
21
+ room text NOT NULL,
22
+ agent text NOT NULL,
23
+ to_number text NOT NULL,
24
+ context jsonb NOT NULL,
25
+ on_end text NOT NULL,
26
+ answered_at timestamptz NOT NULL,
27
+ room_gone_at timestamptz NULL,
28
+ claimed_at timestamptz NULL
29
+ );
@@ -0,0 +1,20 @@
1
+ -- 0119_voice_call_metrics.sql — the latencies of each answered turn of a voice call,
2
+ -- as LiveKit measured them in the voice worker (STA3-56).
3
+ --
4
+ -- The worker sends them once, with the transcript, in the call's `/complete`; the
5
+ -- box writes one row per call and never updates it. It is keyed by the call's
6
+ -- session id, the same one `voice_recordings` uses, so a screen that shows a call
7
+ -- finds its recording and its latencies by one id. A second `/complete` of the same
8
+ -- call shares the first one's result, so the row cannot be written twice; the
9
+ -- insert still skips a conflict rather than fail a completion over a duplicate.
10
+ --
11
+ -- `turns` is the worker's array as the box validated it
12
+ -- (`voiceCallTurnMetricsSchema`): numbers only, no transcript text and nothing that
13
+ -- names the caller, which is why the memory erase does not touch this table.
14
+ CREATE TABLE IF NOT EXISTS stackbone_platform.voice_call_metrics (
15
+ session_id uuid PRIMARY KEY,
16
+ agent text NOT NULL,
17
+ session_key text NOT NULL,
18
+ turns jsonb NOT NULL,
19
+ created_at timestamptz NOT NULL DEFAULT now()
20
+ );
@@ -0,0 +1,19 @@
1
+ -- 0120_run_steps_voice.sql — a `voice` step: one phase of how long a voice turn made
2
+ -- its caller wait (STA3-56).
3
+ --
4
+ -- The voice worker measures each answered turn with LiveKit (end of turn, speech to
5
+ -- text, the agent's first token, speech synthesis, and the whole wait) and sends the
6
+ -- numbers once, in the call's `/complete`. The box then writes them as steps on the
7
+ -- run that answered the turn, so the run's trace shows them on the same time axis
8
+ -- as its `model_call`: a `Wait` parent and one child per phase. Their times come
9
+ -- from the run's own start, the box's clock, so a worker clock that drifts never
10
+ -- moves them.
11
+ ALTER TABLE stackbone_platform.run_steps
12
+ DROP CONSTRAINT IF EXISTS run_steps_type_check;
13
+
14
+ ALTER TABLE stackbone_platform.run_steps
15
+ ADD CONSTRAINT run_steps_type_check
16
+ CHECK (type IN (
17
+ 'workflow_step', 'model_call', 'connection', 'hook',
18
+ 'tool_call', 'agent', 'guardrail', 'voice'
19
+ ));
@@ -0,0 +1,28 @@
1
+ -- 0121_model_routing.sql — the OpenRouter provider routing an operator picked
2
+ -- for one model key of one deep agent (`provider: { sort }` on its model calls).
3
+ --
4
+ -- Addressed like `model_selections` (0073), by (owner_kind, owner_name, key), but
5
+ -- its own table: `model_selections.model_id` is NOT NULL, and an agent that runs
6
+ -- on the model its code names still needs a routing row. No row means the code's
7
+ -- `modelRouting`, and with none written there, OpenRouter's default routing.
8
+ --
9
+ -- `sort` carries NO CHECK, for the reason 0073 has none on `owner_kind`: a box
10
+ -- running an older build must be able to store a value it cannot apply, and the
11
+ -- SDK ignores a sort it does not know.
12
+ CREATE TABLE IF NOT EXISTS stackbone_platform.model_routing (
13
+ owner_kind text NOT NULL,
14
+ owner_name text NOT NULL,
15
+ key text NOT NULL,
16
+ sort text NOT NULL,
17
+ updated_by text,
18
+ updated_at timestamptz NOT NULL DEFAULT now(),
19
+ PRIMARY KEY (owner_kind, owner_name, key)
20
+ );
21
+
22
+ -- The voice settings leaf `fastModelRouting` is retired: provider routing now
23
+ -- belongs to the agent's model keys. The settings schema is strict, so a stored
24
+ -- override still holding the key would fail every read of that agent's settings.
25
+ -- It never reached production, so the value is dropped rather than carried over.
26
+ UPDATE stackbone_platform.voice_settings
27
+ SET overrides = overrides - 'fastModelRouting'
28
+ WHERE overrides ? 'fastModelRouting';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbone/cli",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "bin": {
Binary file