modelstat 0.11.1 → 0.12.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/dist/cli.mjs CHANGED
@@ -5141,7 +5141,19 @@ var init_schemas = __esm({
5141
5141
  source_event_ids: external_exports.array(external_exports.string()).max(2e3),
5142
5142
  /** Optional embedding of the abstract (BGE-small-en-v1.5, 384 dims).
5143
5143
  * Present when the daemon has an Embedder adapter configured. */
5144
- abstract_embedding: external_exports.array(external_exports.number()).length(384).optional()
5144
+ abstract_embedding: external_exports.array(external_exports.number()).length(384).optional(),
5145
+ /** Privacy-preserving on-device behavioral signal — COUNTS/RATIOS ONLY,
5146
+ * never raw text (mirrors RedactionReport). Powers server-side prompt-
5147
+ * friction detection. Optional so older daemons that omit it still validate. */
5148
+ behavior: external_exports.object({
5149
+ /** Developer messages in this segment. */
5150
+ user_turns: external_exports.number().int().nonnegative().default(0),
5151
+ /** User messages that land right after the assistant — a re-prompt /
5152
+ * correction proxy. */
5153
+ correction_count: external_exports.number().int().nonnegative().default(0),
5154
+ /** 0-1 frustration estimate (re-prompt density + negative mood tags). */
5155
+ frustration: external_exports.number().min(0).max(1).default(0)
5156
+ }).optional()
5145
5157
  });
5146
5158
  ToolAction = external_exports.object({
5147
5159
  /** Where it ran: `shell`, `mcp`, `builtin`, `browser`. (tier 0) */
@@ -35265,11 +35277,11 @@ var init_prompts = __esm({
35265
35277
  "use strict";
35266
35278
  OLLAMA_CHAT_MODEL = "qwen3:4b";
35267
35279
  OLLAMA_EMBED_MODEL = "bge-small-en-v1.5";
35268
- SUMMARISER_SYSTEM_PROMPT = "You summarise an AI coding session in ONE sentence, \u2264 240 characters. If the user message includes sampled conversation excerpts, base your summary on what the developer was actually working on (the substance \u2014 what was being built, debugged, refactored, or designed). If only metadata is given, paraphrase the metadata. Never quote the excerpts verbatim. No PII, no code literals, no file paths, no API keys. Reply with only the sentence.";
35269
- SUMMARISER_MAX_TOKENS = 120;
35280
+ SUMMARISER_SYSTEM_PROMPT = 'You summarise an AI coding session in 1-2 sentences, \u2264 400 characters. Name the CONCRETE work: the action taken, WHAT it acted on, and the specific target \u2014 the repository (e.g. erpc/erpc), branch, service, or component \u2014 whenever the session facts or excerpts identify it. Good: "Triggered a GitHub Actions release for erpc/erpc"; "Fixed a null dereference in the auth middleware of api-gateway". Lead with an outcome verb and pack in concrete domain keywords (frameworks, features, decisions). Base the substance on the excerpts when present, else the metadata. Never quote excerpts verbatim. No PII, no secrets, no API keys, no absolute file paths. Reply with only the summary.';
35281
+ SUMMARISER_MAX_TOKENS = 180;
35270
35282
  SUMMARISER_TEMPERATURE = 0.2;
35271
35283
  QWEN_CHARS_PER_TOKEN = 3.3;
35272
- ABSTRACT_OUTPUT_MAX_CHARS = 240;
35284
+ ABSTRACT_OUTPUT_MAX_CHARS = 400;
35273
35285
  }
35274
35286
  });
35275
35287
 
@@ -35782,7 +35794,7 @@ async function summariseSlice(sessionId, slice, adapters2) {
35782
35794
  Sampled excerpts from the conversation (already redacted of PII and secrets):
35783
35795
  ${excerptBlock}
35784
35796
 
35785
- Write the SHORTEST keyword-dense paragraph (1-3 sentences, \u2264${ABSTRACT_OUTPUT_MAX_CHARS} chars) naming exactly what was achieved. Lead with an outcome verb. Pack with concrete domain keywords (frameworks, features, components, decisions). Skip narration and filler.`;
35797
+ Write a \u2264${ABSTRACT_OUTPUT_MAX_CHARS}-char summary (1-2 sentences) naming exactly what was achieved: the concrete action, what it acted on, and the specific target (repo/branch/service/component) when identifiable from the context above. Lead with an outcome verb and pack in concrete domain keywords (frameworks, features, decisions). Skip narration and filler.`;
35786
35798
  const rawAbstract = await adapters2.summarize({
35787
35799
  prompt,
35788
35800
  maxTokens: SUMMARISER_MAX_TOKENS,
@@ -35810,15 +35822,16 @@ Write the SHORTEST keyword-dense paragraph (1-3 sentences, \u2264${ABSTRACT_OUTP
35810
35822
  }
35811
35823
  }
35812
35824
  const redacted = { text: abstractText, counts };
35813
- let abstractWithCognition = redacted.text;
35825
+ let cognition = null;
35814
35826
  if (adapters2.cognize) {
35815
35827
  try {
35816
- const tags2 = await adapters2.cognize({ abstract: redacted.text });
35817
- const suffix = formatCognitionSuffix(tags2);
35818
- if (suffix) abstractWithCognition = `${redacted.text} ${suffix}`;
35828
+ cognition = await adapters2.cognize({ abstract: redacted.text });
35819
35829
  } catch {
35820
35830
  }
35821
35831
  }
35832
+ const cognitionSuffix = cognition ? formatCognitionSuffix(cognition) : "";
35833
+ const abstractWithCognition = cognitionSuffix ? `${redacted.text} ${cognitionSuffix}` : redacted.text;
35834
+ const behavior = computeBehavior(slice, cognition);
35822
35835
  const tags = [
35823
35836
  { root_key: "agents", name: first.agent, confidence: 1 },
35824
35837
  { root_key: "providers", name: first.provider, confidence: 1 }
@@ -35887,7 +35900,35 @@ Write the SHORTEST keyword-dense paragraph (1-3 sentences, \u2264${ABSTRACT_OUTP
35887
35900
  // number-valued catchall for pf_*.
35888
35901
  redaction: redacted.counts,
35889
35902
  source_event_ids: sourceEventIds,
35890
- abstract_embedding: segmentEmbedding && segmentEmbedding.length === 384 ? segmentEmbedding : void 0
35903
+ abstract_embedding: segmentEmbedding && segmentEmbedding.length === 384 ? segmentEmbedding : void 0,
35904
+ behavior
35905
+ };
35906
+ }
35907
+ function computeBehavior(slice, cognition) {
35908
+ let userTurns = 0;
35909
+ let correctionCount = 0;
35910
+ let prevWasAssistant = false;
35911
+ for (const ev of slice) {
35912
+ if (ev.kind === "user_message") {
35913
+ userTurns++;
35914
+ if (prevWasAssistant) correctionCount++;
35915
+ prevWasAssistant = false;
35916
+ } else if (ev.kind === "assistant_message") {
35917
+ prevWasAssistant = true;
35918
+ }
35919
+ }
35920
+ const frustratedMood = cognition?.emotions?.some((e) => {
35921
+ const lower = e.toLowerCase();
35922
+ return FRUSTRATION_MARKERS.some((m) => lower.includes(m));
35923
+ }) ?? false;
35924
+ const frustration = Math.min(
35925
+ 1,
35926
+ Math.max(correctionCount / 4, frustratedMood ? 0.8 : 0)
35927
+ );
35928
+ return {
35929
+ user_turns: userTurns,
35930
+ correction_count: correctionCount,
35931
+ frustration: Math.round(frustration * 100) / 100
35891
35932
  };
35892
35933
  }
35893
35934
  function sampleAndRedactExcerpts(slice) {
@@ -35942,7 +35983,7 @@ function inferEnvironment(branch) {
35942
35983
  if (b === "dev" || b === "develop" || b.startsWith("dev/")) return "Dev";
35943
35984
  return null;
35944
35985
  }
35945
- var SEGMENT_TIME_GAP_MS, SEGMENT_TOPIC_THRESHOLD, SEGMENT_MAX_TURNS, SEGMENT_MAX_DURATION_MS, SEGMENT_MAX_CONTENT_CHARS, ABSTRACT_MAX_CHARS;
35986
+ var SEGMENT_TIME_GAP_MS, SEGMENT_TOPIC_THRESHOLD, SEGMENT_MAX_TURNS, SEGMENT_MAX_DURATION_MS, SEGMENT_MAX_CONTENT_CHARS, ABSTRACT_MAX_CHARS, FRUSTRATION_MARKERS;
35946
35987
  var init_pipeline = __esm({
35947
35988
  "../../packages/daemon-core/src/pipeline/index.ts"() {
35948
35989
  "use strict";
@@ -35964,6 +36005,17 @@ var init_pipeline = __esm({
35964
36005
  SEGMENT_MAX_DURATION_MS = 30 * 6e4;
35965
36006
  SEGMENT_MAX_CONTENT_CHARS = 12e3;
35966
36007
  ABSTRACT_MAX_CHARS = 512;
36008
+ FRUSTRATION_MARKERS = [
36009
+ "frustrat",
36010
+ "annoy",
36011
+ "stuck",
36012
+ "confus",
36013
+ "irritat",
36014
+ "block",
36015
+ "stress",
36016
+ "angr",
36017
+ "overwhelm"
36018
+ ];
35967
36019
  }
35968
36020
  });
35969
36021
 
@@ -37267,7 +37319,7 @@ var init_scan = __esm({
37267
37319
  init_api();
37268
37320
  init_config2();
37269
37321
  init_pipeline2();
37270
- DAEMON_VERSION = true ? "daemon-0.11.1" : "daemon-dev";
37322
+ DAEMON_VERSION = true ? "daemon-0.12.0" : "daemon-dev";
37271
37323
  BATCH_MAX_EVENTS = INGEST_BATCH_MAX_EVENTS;
37272
37324
  BATCH_MAX_TOOL_CALLS = 2e4;
37273
37325
  BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
@@ -37834,7 +37886,7 @@ var PROCESSING_VERSION;
37834
37886
  var init_processing_version = __esm({
37835
37887
  "src/processing-version.ts"() {
37836
37888
  "use strict";
37837
- PROCESSING_VERSION = 9;
37889
+ PROCESSING_VERSION = 10;
37838
37890
  }
37839
37891
  });
37840
37892
 
@@ -40214,7 +40266,7 @@ var init_daemon = __esm({
40214
40266
  init_scan();
40215
40267
  init_single_flight();
40216
40268
  init_update();
40217
- DAEMON_VERSION2 = true ? "daemon-0.11.1" : "daemon-dev";
40269
+ DAEMON_VERSION2 = true ? "daemon-0.12.0" : "daemon-dev";
40218
40270
  HEARTBEAT_INTERVAL_MS = 1e4;
40219
40271
  SCAN_INTERVAL_MS = 5 * 60 * 1e3;
40220
40272
  DISCOVERY_INTERVAL_MS = 6e4;
@@ -40846,7 +40898,7 @@ function tryOpenBrowser(url) {
40846
40898
  return false;
40847
40899
  }
40848
40900
  }
40849
- var DAEMON_VERSION3 = true ? "daemon-0.11.1" : "daemon-dev";
40901
+ var DAEMON_VERSION3 = true ? "daemon-0.12.0" : "daemon-dev";
40850
40902
  function osFamily() {
40851
40903
  const p = platform6();
40852
40904
  if (p === "darwin") return "macos";