argus-decision-mcp 1.11.0 → 1.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.
@@ -46,10 +46,22 @@ export function learnLocaleFromContent(argusDir, args) {
46
46
  * month precisely because nothing ever said "your config disagrees with
47
47
  * your words"). */
48
48
  export function contentLocaleFromArgs(args) {
49
- const sample = CONTENT_FIELDS
49
+ const parts = CONTENT_FIELDS
50
50
  .map((k) => args[k])
51
- .filter((v) => typeof v === 'string' && v.trim().length > 0)
52
- .join('\n');
51
+ .filter((v) => typeof v === 'string' && v.trim().length > 0);
52
+ // User-authored words also arrive NESTED: a premises op=add call carries
53
+ // them only in premises[].text, invisible to the flat sample above — so a
54
+ // Korean add-premises call was answered in English until a later flat-field
55
+ // call re-taught the session (content battery S06, 2026-07-27).
56
+ const prems = args['premises'];
57
+ if (Array.isArray(prems)) {
58
+ for (const p of prems) {
59
+ const t = p?.['text'];
60
+ if (typeof t === 'string' && t.trim().length > 0)
61
+ parts.push(t);
62
+ }
63
+ }
64
+ const sample = parts.join('\n');
53
65
  return sample ? detectLocaleFromText(sample) : null;
54
66
  }
55
67
  /**
package/dist/lib/spine.js CHANGED
@@ -56,11 +56,11 @@ export const SERVER_INSTRUCTIONS = [
56
56
  '',
57
57
  'Run these three as a CONTINUOUS BACKGROUND SENSE over the whole conversation — never as a checklist you make the user complete:',
58
58
  '',
59
- '1. NOTICE A PREDICTION AS IT PASSES. When the user, doing their work, makes a claim reality can later check — "this should cut costs ~10%", "we\'ll ship Friday", "churn drops once we launch" — that IS a prediction, whether or not they call it one. The tell is a claim that pairs a direction or target with a horizon or a number (a "will / should / by-Friday" plus something measurable, or a ship/close/hire event). But it must be a claim the user COMMITS TO as their own: a question they put to YOU — which option is better, whether something will work, "can you draft / build / configure X" — is soliciting your help, not a prediction they are making, however weighty the topic; answer it and stay out. Do not wait to be told "save this." On a CONSEQUENTIAL one, offer ONCE: turn it into a falsifiable line in THEIR words (e.g. "ship Friday" → "shipped to TestFlight by 2026-07-10"), take a check-by from the horizon they named, and call argus_predict with confirm_draft:true — the one-tap confirm picker (Accept to keep, or reword / adjust the date, Decline to skip) IS the ask; never ask "want me to save this?" in prose and wait. When they dictate one word-for-word, just save it (predicate_owner:"user"). On a trivial or flat call, stay quiet; a blank is honest. Record exactly ONE falsifiable claim per predicate: if the turn carries two (e.g. a ship date AND closing deals, or two separate numbers), keep only the single most load-bearing one — never conjoin them with "and"/"so" into one predicate, which stacks two predictions that cannot settle cleanly. Notice it ONLY as it is spoken this turn: if the claim surfaced turns ago and the user has since moved to a different topic, the moment has passed — never reach back to resurface a past turn\'s claim mid-new-topic (a stale offer reads as not listening).',
59
+ '1. NOTICE A PREDICTION AS IT PASSES. When the user, doing their work, makes a claim reality can later check — "this should cut costs ~10%", "we\'ll ship Friday", "churn drops once we launch" — that IS a prediction, whether or not they call it one. The tell is a claim that pairs a direction or target with a horizon or a number (a "will / should / by-Friday" plus something measurable, or a ship/close/hire event). But it must be a claim the user COMMITS TO as their own: a question they put to YOU — which option is better, whether something will work, "can you draft / build / configure X" — is soliciting your help, not a prediction they are making, however weighty the topic; answer it and stay out. Do not wait to be told "save this." On a CONSEQUENTIAL one, offer ONCE: turn it into a falsifiable line in THEIR words (e.g. "ship Friday" → "shipped to TestFlight by 2026-07-10"), take a check-by from the horizon they named, and call argus_predict with confirm_draft:true — the one-tap confirm picker (Accept to keep, or reword / adjust the date, Decline to skip) IS the ask; never ask "want me to save this?" in prose and wait, and never DOUBLE-ask (a chat question beside the picker asks twice — any chat line accompanying the picker states what you noticed, it does not ask). Falsifiable means a stranger could mark it true or false later from observable facts: sharpen a vague verb ("flops", "잘 된다") into the measurable the user already named — a number, a date, a named event — and if they named none, anchor to the nearest observable comparison ("beats last launch\'s first-week signups"), still in their words. When they dictate one word-for-word, just save it (predicate_owner:"user"). On a trivial or flat call, stay quiet; a blank is honest. Record exactly ONE falsifiable claim per predicate: if the turn carries two (e.g. a ship date AND closing deals, or two separate numbers), keep only the single most load-bearing one — never conjoin them with "and"/"so" into one predicate, which stacks two predictions that cannot settle cleanly. Notice it ONLY as it is spoken this turn: if the claim surfaced turns ago and the user has since moved to a different topic, the moment has passed — never reach back to resurface a past turn\'s claim mid-new-topic (a stale offer reads as not listening).',
60
60
  '',
61
61
  '2. NOTICE THE OUTCOME AS IT SURFACES. This is the whole reason you live in the conversation. You already know which predictions are open (argus_check_in at the session start, or read the ledger). So when the user\'s ongoing work reveals what reality did — "oh, the migration went fine", "we missed the number", "we shipped Tuesday" — recognize that it settles a tracked prediction and record it THEN with argus_resolve, in the user\'s own words. Do NOT make them come back to report, and do NOT wait for the check-by date when reality has already spoken in the conversation. Never infer an outcome the user did not state; if reality has not answered, leave it open (or argus_resolve outcome:"still_pending" with defer_to). Recording an outcome is neutral bookkeeping — never praise, never grade.',
62
62
  '',
63
- '3. NOTICE THE LOAD-BEARING ASSUMPTION. A consequential decision rests on something — and it counts even when the user hands it to you as work to execute ("here is the plan, start with X" is still a plan whose load-bearing premise you can notice while you do the task). The tell is a conditional the call would flip on if it broke — an "as long as / because / only if / assuming" clause. If the user reasoned it out loud, catch the single assumption it most rests on in their own words (argus_capture action=open or add_context). If they did not spell it out, offer ONE sharp candidate as your draft — exactly one premise in one clause, never two stacked (predicate_owner/source "ai_surfaced", your wording preserved) for a one-tap confirm — never a guess passed off as their fact, never more than one. The tells of stacking to avoid: an "and", a "so"/"therefore", or a second fact/date/number in the premise — split them and keep only the ONE the decision most rests on. The load-bearing premise is usually NOT the reason they said out loud: the stated reason is the surface, and the real premise is the specific, often-unstated fact the decision REVERSES on if it turns out false. ("Drop the free tier because it is too generous" rests on the unstated bet that squeezed free users convert to paid, not that free is what drives signups; "defer billing to next sprint" rests on the bet that nobody churns while it is absent; "we are fine as long as the vendor deal holds" rests on whether that deal is actually locked in, not on the volume continuing.) Name that specific mechanism in concrete, falsifiable terms — not a generic restatement of their rationale, and not a paraphrase of the clause they already spoke. Update a changed external fact with argus_capture action=update_fact and honest provenance; state the fact, and whether to revisit is the user\'s call. Draft it only for the decision in the user\'s CURRENT message — never reach back to surface an assumption from an earlier turn the user has already moved past; once they are on a new topic, that decision\'s moment is closed.',
63
+ '3. NOTICE THE LOAD-BEARING ASSUMPTION. A consequential decision rests on something — and it counts even when the user hands it to you as work to execute ("here is the plan, start with X" is still a plan whose load-bearing premise you can notice while you do the task). The tell is a conditional the call would flip on if it broke — an "as long as / because / only if / assuming" clause. If the user reasoned it out loud, catch the single assumption it most rests on in their own words (argus_capture action=open or add_context). If they did not spell it out, offer ONE sharp candidate as your draft — exactly one premise in one clause, never two stacked (predicate_owner/source "ai_surfaced", your wording preserved) for a one-tap confirm — the confirm IS the ask, so no parallel chat question — never a guess passed off as their fact, never more than one. The tells of stacking to avoid: an "and", a "so"/"therefore", or a second fact/date/number in the premise — split them and keep only the ONE the decision most rests on. The load-bearing premise is usually NOT the reason they said out loud: the stated reason is the surface, and the real premise is the specific, often-unstated fact the decision REVERSES on if it turns out false. ("Drop the free tier because it is too generous" rests on the unstated bet that squeezed free users convert to paid, not that free is what drives signups; "defer billing to next sprint" rests on the bet that nobody churns while it is absent; "we are fine as long as the vendor deal holds" rests on whether that deal is actually locked in, not on the volume continuing.) Name that specific mechanism in concrete, falsifiable terms — not a generic restatement of their rationale, and not a paraphrase of the clause they already spoke. Update a changed external fact with argus_capture action=update_fact and honest provenance; state the fact, and whether to revisit is the user\'s call. Draft it only for the decision in the user\'s CURRENT message — never reach back to surface an assumption from an earlier turn the user has already moved past; once they are on a new topic, that decision\'s moment is closed.',
64
64
  '',
65
65
  'Never break the spine while sensing:',
66
66
  '- No verdict, ever. No "you were right/wrong", no "the stronger case is X", no disclaimed lean. There is no verdict tool because there is no verdict to give.',
@@ -69,7 +69,10 @@ export function lintEnvelope(env) {
69
69
  if (hasUnsafeChars(surface)) {
70
70
  out.push({ severity: 'red', rule: 'unsafe-chars', message: 'surface carries control/bidi/zero-width characters (terminal-escape or homograph injection vector)' });
71
71
  }
72
- if (surface.length > SURFACE_MAX) {
72
+ // A keepsake card (seal plate / judgment receipt / logbook — the boxed
73
+ // '┌─ ARGUS' artifacts) is deliberately bigger than a line; length-lint
74
+ // only the prose surfaces.
75
+ if (surface.length > SURFACE_MAX && !surface.includes('┌─ ARGUS')) {
73
76
  out.push({ severity: 'yellow', rule: 'surface-too-long', message: `surface is ${surface.length} chars (>${SURFACE_MAX}) — a line, not a paragraph`, excerpt: surface.slice(0, 140) });
74
77
  }
75
78
  }
@@ -126,7 +126,10 @@ export const SURFACES = {
126
126
  footer: 'argus · prediction saved → result recorded ⚓',
127
127
  },
128
128
  wake: {
129
- header: 'ARGUS · YOUR DECISIONS',
129
+ // The logbook: what a ship keeps of its voyage — dates, positions, what
130
+ // happened. Identity without ceremony (창업자 2026-07-27: 터미널에도
131
+ // 항해 정체성을, 과하지 않게).
132
+ header: 'ARGUS · LOGBOOK',
130
133
  counts: (total, sealed, settled) => `decisions ${total} · awaiting check ${sealed} · results recorded ${settled}`,
131
134
  overdue_group: (n) => `past check-by (${n})`,
132
135
  overdue_hint: '← argus_resolve',
@@ -136,7 +139,7 @@ export const SURFACES = {
136
139
  settled_group: (n, held, avoided, partial, missed) => `results recorded (${n}): held ${held} · avoided ${avoided} · partial ${partial} · missed ${missed}`,
137
140
  outcome_label: (o) => ({ held: 'held', avoided: 'avoided', partial: 'partial', missed: 'missed', still_pending: 'pending' })[o] ?? o,
138
141
  more: (n) => `… (+${n})`,
139
- record_since: (date) => `on record since ${date}`,
142
+ record_since: (date) => `logbook since ${date}`,
140
143
  },
141
144
  receipt: {
142
145
  header: 'ARGUS · JUDGMENT RECEIPT',
@@ -277,7 +280,7 @@ export const SURFACES = {
277
280
  footer: 'argus · 예측 저장 → 실제 결과 기록 ⚓',
278
281
  },
279
282
  wake: {
280
- header: 'ARGUS · 결정 기록',
283
+ header: 'ARGUS · 항해일지',
281
284
  // "예측 저장 0"은 정산이 끝나면 0으로 줄어 "저장한 적 없음"처럼 읽혔다
282
285
  // (1.4.6 재진단): 이 칸의 의미는 '지금 확인일을 기다리는 것'이다.
283
286
  counts: (total, sealed, settled) => `결정 ${total} · 확인 대기 ${sealed} · 결과 기록 ${settled}`,
@@ -289,7 +292,7 @@ export const SURFACES = {
289
292
  settled_group: (n, held, avoided, partial, missed) => `결과 기록됨 (${n}): 예측대로 ${held} · 걱정 피함 ${avoided} · 일부 ${partial} · 빗나감 ${missed}`,
290
293
  outcome_label: (o) => ({ held: '예측대로', avoided: '걱정 피함', partial: '일부', missed: '빗나감', still_pending: '대기' })[o] ?? o,
291
294
  more: (n) => `… (+${n})`,
292
- record_since: (date) => `${date}부터 기록`,
295
+ record_since: (date) => `${date}부터 항해 중`,
293
296
  },
294
297
  receipt: {
295
298
  header: 'ARGUS · 판단 영수증',
@@ -327,7 +330,7 @@ export const SURFACES = {
327
330
  factual: '이건 답이 있는 질문이지, 열어둘 결정이 아닙니다.',
328
331
  already_closed: '이미 내린 결정입니다. Argus는 이걸 다시 열지 않습니다.',
329
332
  flat: '선택지가 거의 대등합니다. 억지로 만들 핵심 질문이 없습니다.',
330
- reversible_low_stakes: '되돌리기 쉽고 크게 걸린 것도 없는 결정이에요.',
333
+ reversible_low_stakes: '되돌리기 쉽고 크게 걸린 것도 없는 결정입니다.',
331
334
  low_stakes: '걸린 것이 별로 없습니다. 그대로 두는 편이 무난합니다.',
332
335
  },
333
336
  reason_fallback: '여기서 억지로 지어낼 결정은 없습니다.',
@@ -335,7 +338,7 @@ export const SURFACES = {
335
338
  watch_exit: ' 기록하지 않고 그대로 두어도 괜찮습니다.',
336
339
  reconfirm: '신호가 서로 어긋납니다 (걸린 것은 큰데 되돌리기는 쉽습니다). 더 나아가기 전에 이 둘을 다시 짚어 보세요.',
337
340
  opened_with_crux: (crux) => `열었습니다. 이 결정을 좌우하는 단 하나의 질문: ${crux}`,
338
- opened_bare: '이 결정, 기록해뒀어요.',
341
+ opened_bare: '이 결정, 기록해뒀습니다.',
339
342
  lean_disclosure: '핵심 질문을 짚는 것 자체가 뒤집히는 쪽을 희미하게 가리킬 수 있습니다. 그렇게 남는 쏠림은 알려진 한계일 뿐, 이 결정에 대한 평가가 아닙니다.',
340
343
  },
341
344
  seal: {
@@ -349,7 +352,7 @@ export const SURFACES = {
349
352
  settled: (outcome, predicate) => `실제 결과를 기록했습니다: ${({ held: '예측대로 됐다', avoided: '걱정한 일은 안 일어났다', partial: '일부만 맞았다', missed: '예측이 빗나갔다' })[outcome]}.${predicate ? ` (예측: "${predicate}".)` : ''} 영수증에 예측과 실제가 나란히 남습니다. 평가는 없습니다.`,
350
353
  sync_failed: (reason) => ` (계정 동기화가 안 됐습니다. ${reason}. 결과는 로컬에 안전합니다. 동기화되기 전까지 계정은 이걸 계속 "확인 필요"로 표시할 수 있습니다. 나중에 argus_settings action=sync를 시도하세요.)`,
351
354
  deferred: (newDate) => `아직 결과를 기록하지 않았습니다. 현실이 답하지 않았으니 평가한 것도 없습니다. ${newDate}에 다시 가져오겠습니다.`,
352
- defer_dismissed: '접어뒀습니다. 이건 이제 답이 필요 없어요. 평가한 것은 없습니다.',
355
+ defer_dismissed: '접어뒀습니다. 이건 이제 답이 필요 없습니다. 평가한 것은 없습니다.',
353
356
  },
354
357
  recheck: {
355
358
  baseline: (ref, finding, source, cadenceDays) => `P${ref} 기준값을 기록했습니다: "${finding}" (${source}). ${cadenceDays}일 뒤에 다시 확인하길 권합니다.`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argus-decision-mcp",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Argus decision-accountability MCP server — clarify a decision, save a falsifiable prediction, and record what reality did. The model never grades you.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",