clementine-agent 1.16.0 → 1.17.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.
@@ -4769,13 +4769,28 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
4769
4769
  // Periodic progress beacon — sends a status update every 5 minutes
4770
4770
  // so the user knows the task is still alive during long phases.
4771
4771
  // Capped at 3 messages per phase to prevent notification spam.
4772
+ // Also refreshes status.json so dashboard polls see liveness even
4773
+ // when the SDK stream hasn't emitted a result yet.
4772
4774
  const BEACON_INTERVAL_MS = 5 * 60 * 1000;
4773
4775
  const MAX_BEACONS_PER_PHASE = 3;
4774
4776
  let beaconCount = 0;
4775
4777
  const beaconTimer = setInterval(() => {
4778
+ const mins = Math.round((Date.now() - phaseStart) / 60_000);
4779
+ try {
4780
+ writeStatus({
4781
+ jobName,
4782
+ status: 'running',
4783
+ phase,
4784
+ startedAt,
4785
+ maxHours: effectiveMaxHours,
4786
+ phaseStartedAt: new Date(phaseStart).toISOString(),
4787
+ phaseElapsedMin: mins,
4788
+ toolCallsThisPhase: phaseToolCount,
4789
+ });
4790
+ }
4791
+ catch { /* non-fatal */ }
4776
4792
  if (this.onPhaseProgress && beaconCount < MAX_BEACONS_PER_PHASE) {
4777
4793
  beaconCount++;
4778
- const mins = Math.round((Date.now() - phaseStart) / 60_000);
4779
4794
  try {
4780
4795
  // Conversational beacon — no technical jargon
4781
4796
  const msg = mins < 3
@@ -4826,6 +4841,22 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
4826
4841
  // Capture terminal reason for execution advisor
4827
4842
  this._lastTerminalReason = result.terminal_reason ?? undefined;
4828
4843
  this.logQueryResult(result, 'unleashed', `unleashed:${jobName}`, jobName);
4844
+ // Refresh status.json the moment the SDK reports result —
4845
+ // even if the underlying stream stalls afterward, the dashboard
4846
+ // sees liveness instead of a frozen "phase 0 / running" row.
4847
+ try {
4848
+ writeStatus({
4849
+ jobName,
4850
+ status: 'running',
4851
+ phase,
4852
+ startedAt,
4853
+ maxHours: effectiveMaxHours,
4854
+ lastResultAt: new Date().toISOString(),
4855
+ lastResultIsError: !!result.is_error,
4856
+ toolCallsThisPhase: phaseToolCount,
4857
+ });
4858
+ }
4859
+ catch { /* non-fatal */ }
4829
4860
  // Detect dollar-budget exceeded (strict marker — see cron
4830
4861
  // handler above for the reasoning).
4831
4862
  if (result.is_error && 'result' in result) {
@@ -50,6 +50,21 @@ const CHAIN_MARKERS = [
50
50
  /\bonce\s+(that|you)\b.*,/i,
51
51
  /\bnext\b.*,/i,
52
52
  ];
53
+ /**
54
+ * Patterns that look like a pasted error message or stack trace.
55
+ * Error pastes are long and entity-heavy (file paths, quoted strings,
56
+ * "Error:" prefixes), which previously tripped the deepWorthy gate
57
+ * even when the user was just asking "what's wrong with this?". We
58
+ * still allow the plan-first directive to fire; we just don't auto-spawn
59
+ * an expensive multi-phase background task on a debug request.
60
+ */
61
+ const ERROR_PASTE_MARKERS = [
62
+ /\b(Error|Exception|Traceback|Stack ?trace):\s/i,
63
+ /^\s*at\s+[\w.$<>]+\s*\(/m, // JS/TS stack frame: "at foo.bar (file:line)"
64
+ /\bfailed:\s*Error\b/i,
65
+ /Reached maximum number of turns/i,
66
+ /\bENOENT\b|\bECONNREFUSED\b|\bETIMEDOUT\b/,
67
+ ];
53
68
  /**
54
69
  * Phrasings that explicitly ask for plan-first behavior. Triggers
55
70
  * regardless of other heuristics.
@@ -148,7 +163,14 @@ export function classifyComplexity(text) {
148
163
  isLong,
149
164
  entities >= 3,
150
165
  ].filter(Boolean).length;
151
- const deepWorthy = strongCount >= 2;
166
+ // Suppress deepWorthy on pasted error messages. They're long and
167
+ // entity-heavy (file paths, quoted strings) but the user is asking
168
+ // "what's wrong here?", not requesting sustained autonomous work.
169
+ // The plan-first path still fires when complex=true.
170
+ const looksLikeErrorPaste = ERROR_PASTE_MARKERS.some((re) => re.test(trimmed));
171
+ if (looksLikeErrorPaste)
172
+ signals.push('error-paste');
173
+ const deepWorthy = strongCount >= 2 && !looksLikeErrorPaste;
152
174
  if (complex) {
153
175
  return {
154
176
  complex: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",