@inetafrica/open-claudia 3.4.15 → 3.4.16

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.4.16
4
+
5
+ - Start live-call answer generation without waiting for an auxiliary memory-relevance model. Calls keep local matches from the current utterance, bounded spoken history, and full policy; context-only keyword matches remain excluded without a relevance judgment. Ordinary chat filtering and explicit on-demand recall are unchanged.
6
+ - Log live-call provider preparation, prompt readiness, process start and completion with elapsed time, so recognition delay can be distinguished from reply startup and generation.
7
+ - Reproduce the call-path regression through the actual prompt builder and classic matcher, covering both configured recall engines, context-only exclusions, ordinary chat filtering and on-demand recall.
8
+
3
9
  ## 3.4.15
4
10
 
5
11
  - Preserve complete Kazee speech turns with protocol-2 segment matching, bounded pending finals, and cancellation when speech resumes during an endpoint decision. Keep compatibility with older speech servers, including a delayed protocol-ready message.
package/core/runner.js CHANGED
@@ -1936,6 +1936,10 @@ function createStreamPreview({ state, store }) {
1936
1936
 
1937
1937
  function createForegroundService({ state, store, stopTyping, capture, ownsAdmissionLock, persistCapture = true }) {
1938
1938
  const callSpeechLeases = new Map();
1939
+ const callStage = (stage, runContext) => {
1940
+ if (capture || runContext?.liveCall !== true) return;
1941
+ console.log(`${new Date().toISOString()} Kazee call provider: stage=${stage} run=${runContext.runId} elapsed=${Math.max(0, Date.now() - runContext.admittedAt)}ms`);
1942
+ };
1939
1943
  const persistEffects = !capture || persistCapture;
1940
1944
  const preview = capture ? null : createStreamPreview({ state, store });
1941
1945
  // Chat-visibility + learning-signal layer (skill/pack/tool announcements,
@@ -1946,7 +1950,10 @@ function createForegroundService({ state, store, stopTyping, capture, ownsAdmiss
1946
1950
  getStore: () => store,
1947
1951
  isObsolete: () => obsoleteCallTurn(store),
1948
1952
  resolveProvider: resolveForegroundProvider,
1949
- buildTurnPrompt,
1953
+ buildTurnPrompt(prompt, opts) {
1954
+ callStage("preparing", opts.runContext);
1955
+ return buildTurnPrompt(prompt, opts);
1956
+ },
1950
1957
  buildInvocation(provider, context) {
1951
1958
  if (context.purpose === "compact-seed" && typeof provider.buildCompactionInvocation === "function") {
1952
1959
  return provider.buildCompactionInvocation(context);
@@ -1969,6 +1976,7 @@ function createForegroundService({ state, store, stopTyping, capture, ownsAdmiss
1969
1976
  persistSession: persistEffects ? ((result, runContext) => persistForegroundSession(result, runContext, state)) : null,
1970
1977
  deliver: capture ? null : (result, runContext) => deliverForegroundResult(result, runContext, state),
1971
1978
  onSpawn(proc, runContext) {
1979
+ callStage("started", runContext);
1972
1980
  callSpeechLeases.get(runContext.runId)?.activate();
1973
1981
  state.runningProcess = proc;
1974
1982
  if (ownsAdmissionLock) state.preparingRun = false;
@@ -1980,10 +1988,12 @@ function createForegroundService({ state, store, stopTyping, capture, ownsAdmiss
1980
1988
  return true;
1981
1989
  },
1982
1990
  onPromptBundle(bundle, runContext) {
1991
+ callStage("prompt-ready", runContext);
1983
1992
  if (!observer || runContext.purpose !== "foreground") return;
1984
1993
  observer.onRecallSurfaced(bundle.recallMetadata);
1985
1994
  },
1986
1995
  onEvent(event, runContext) {
1996
+ if (event.type === "result" || event.type === "error") callStage("finished", runContext);
1987
1997
  if (event.type === "result" || event.type === "error") callSpeechLeases.get(runContext.runId)?.close();
1988
1998
  // Streaming text reads as "typing"; tool work re-enters "thinking" so
1989
1999
  // the chip doesn't decay to a stale "typing" through long tool stretches.
@@ -997,6 +997,10 @@ async function filterMatches(userText, contextText, packMatches, entityMatches,
997
997
  packMatches: packMatches.filter((m) => m.origin === "user"),
998
998
  entityMatches: entityMatches.filter((m) => m.origin === "user"),
999
999
  });
1000
+ // The classic engine still uses an auxiliary model. On a live call that
1001
+ // adds seconds before the answering model can even start. Keep the local
1002
+ // user-keyword baseline; context-only hits still need a relevance judge.
1003
+ if (opts.liveCall === true) return failOpen();
1000
1004
  if (packMatches.length === 0 && entityMatches.length === 0) {
1001
1005
  return { packMatches, entityMatches };
1002
1006
  }
@@ -1176,12 +1180,12 @@ async function buildOptionalRecallContext(prompt, opts = {}) {
1176
1180
  const budget = memoryRecallBudget();
1177
1181
  const settings = promptState(opts).settings || {};
1178
1182
  const recall = require("./recall");
1179
- // Live call: the discoverer's LLM walk is many seconds of dead air the
1180
- // caller sits through, so calls downgrade it to classic (~50ms keyword
1181
- // baseline). The ask engine is already zero-latency (injects nothing, model
1182
- // pulls on demand and narrates the lookup), so it rides through unchanged.
1183
+ // Calls use classic matching with the auxiliary relevance model disabled
1184
+ // below. Merely selecting classic still waits on that model. Explicit ask
1185
+ // mode continues to retrieve memory on demand.
1186
+ const liveCall = opts.runContext?.liveCall ?? (currentAdapter()?.liveCall === true);
1183
1187
  const activeName = recall.activeEngineName(settings);
1184
- const engineName = currentAdapter()?.liveCall === true && activeName === "discoverer" ? "classic" : activeName;
1188
+ const engineName = liveCall === true && activeName === "discoverer" ? "classic" : activeName;
1185
1189
  const engine = recall.getEngine(engineName);
1186
1190
  const recallOpts = { ...opts, recallCollector: collector };
1187
1191
  const recallProvider = promptRunIdentity({ ...opts, state: promptState(opts) }).provider;
@@ -1194,7 +1198,7 @@ async function buildOptionalRecallContext(prompt, opts = {}) {
1194
1198
  context,
1195
1199
  packMatches,
1196
1200
  entityMatches,
1197
- { provider: recallProvider },
1201
+ { provider: recallProvider, liveCall },
1198
1202
  ),
1199
1203
  logRecall,
1200
1204
  buildPackBlock: (matches, recallBudget, engineOpts) => buildPackBlock(matches, recallBudget, { ...recallOpts, ...(engineOpts || {}) }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.4.15",
3
+ "version": "3.4.16",
4
4
  "description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
5
5
  "main": "bot.js",
6
6
  "bin": {
@@ -468,7 +468,8 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
468
468
  // 3.4.13: Codex usage accounting fix — Sumeet approved the three-bug fix on 2026-09-05 ("Yes");
469
469
  // prepared 2026-09-06 for his release go. Rollout-based Codex context, per-turn ledger, sub-agent + quota accounting.
470
470
  // 3.4.15: conversation flow and checked sentence speech; authorized calling rollout, 2026-09-05/06.
471
- assert.strictEqual(pkg.version, "3.4.15", "release version must remain unchanged without explicit approval");
471
+ // 3.4.16: remove the auxiliary recall-model delay from live replies; Sumeet's approved calling rollout, 2026-09-05/06.
472
+ assert.strictEqual(pkg.version, "3.4.16", "release version must remain unchanged without explicit approval");
472
473
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
473
474
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
474
475
  }
@@ -253,6 +253,52 @@ async function main() {
253
253
  recall.getEngine = originalGetEngine;
254
254
  assert.strictEqual(admittedProvider, "codex");
255
255
 
256
+ // Real call prompt -> classic matcher -> relevance filter. A second model
257
+ // must not hold a live reply; context-only hits still require a judge and
258
+ // cannot be promoted just because calls use the local keyword baseline.
259
+ const packsLib = require("./core/packs");
260
+ const entitiesLib = require("./core/entities");
261
+ const transcriptsLib = require("./core/transcripts");
262
+ const filterLib = require("./core/recall-filter");
263
+ const saved = { matchPacks: packsLib.matchPacks, matchEntities: entitiesLib.matchEntities,
264
+ recentTranscriptText: transcriptsLib.recentTranscriptText, judgeRelevance: filterLib.judgeRelevance };
265
+ const { chatContext } = require("./core/context");
266
+ let judgeCalls = 0;
267
+ try {
268
+ packsLib.matchPacks = text => [{ dir: text === "Current deployment" ? "current-topic" : "stale-chat-topic", score: 10 }];
269
+ entitiesLib.matchEntities = () => [];
270
+ transcriptsLib.recentTranscriptText = () => "Earlier unrelated chat discussion";
271
+ filterLib.judgeRelevance = async () => { judgeCalls++; return new Set(["pack:current-topic", "pack:stale-chat-topic"]); };
272
+ for (const recallEngine of ["discoverer", "classic"]) {
273
+ const callRecall = await chatContext.run({ adapter: { type: "kazee", liveCall: true } }, () =>
274
+ systemPrompt.buildOptionalRecallContext("Current deployment", {
275
+ state: { ...mutableState, settings: { backend: "codex", recallEngine } },
276
+ runContext: { provider: "codex", liveCall: true, purpose: "foreground", project: { name: "project-a", dir: root }, runId: `call-${recallEngine}` },
277
+ }));
278
+ assert.strictEqual(judgeCalls, 0, "live call must reach answer generation without a second model for recall");
279
+ assert.deepStrictEqual(callRecall.metadata.packDirs, ["current-topic"], "retain current-topic keyword memory, exclude stale context-only matches");
280
+ }
281
+ const ordinaryRecall = await chatContext.run({ adapter: { type: "kazee" } }, () =>
282
+ systemPrompt.buildOptionalRecallContext("Current deployment", {
283
+ state: { ...mutableState, settings: { backend: "codex", recallEngine: "classic" } },
284
+ runContext: { provider: "codex", purpose: "foreground", project: { name: "project-a", dir: root }, runId: "ordinary-classic" },
285
+ }));
286
+ assert.strictEqual(judgeCalls, 1, "ordinary chat retains its model-based relevance filter");
287
+ assert.deepStrictEqual(ordinaryRecall.metadata.packDirs, ["current-topic", "stale-chat-topic"]);
288
+ const askRecall = await chatContext.run({ adapter: { type: "kazee", liveCall: true } }, () =>
289
+ systemPrompt.buildOptionalRecallContext("Current deployment", {
290
+ state: { ...mutableState, settings: { backend: "codex", recallEngine: "ask" } },
291
+ runContext: { provider: "codex", liveCall: true, purpose: "foreground", project: { name: "project-a", dir: root }, runId: "call-ask" },
292
+ }));
293
+ assert.strictEqual(askRecall.metadata.engine, "ask", "explicit on-demand recall remains on-demand during calls");
294
+ assert.strictEqual(judgeCalls, 1);
295
+ } finally {
296
+ packsLib.matchPacks = saved.matchPacks;
297
+ entitiesLib.matchEntities = saved.matchEntities;
298
+ transcriptsLib.recentTranscriptText = saved.recentTranscriptText;
299
+ filterLib.judgeRelevance = saved.judgeRelevance;
300
+ }
301
+
256
302
  // ── compaction recall policy: the summary meta-turn skips recall entirely;
257
303
  // the seed turn matches on the runner-supplied recallQuery (condensed
258
304
  // brief + task titles), never the seed prompt's boilerplate ──