@causyne/agent 1.10.21 → 1.11.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.
Files changed (55) hide show
  1. package/native-skills/media/professional-document-publishing/SKILL.md +44 -0
  2. package/native-skills/media/screenplay-and-shotlist-forge/SKILL.md +42 -0
  3. package/package.json +1 -1
  4. package/src/acp/client.js +235 -0
  5. package/src/acp/client.ts +282 -0
  6. package/src/acp/peerProfiles.js +122 -0
  7. package/src/acp/peerProfiles.ts +128 -0
  8. package/src/acp/protocol.js +54 -0
  9. package/src/acp/protocol.ts +434 -0
  10. package/src/acp/server.js +609 -0
  11. package/src/acp/server.ts +676 -0
  12. package/src/agentLoop.d.ts +43 -0
  13. package/src/agentLoop.js +142 -8
  14. package/src/commands.js +203 -0
  15. package/src/compiledSkills.js +10 -0
  16. package/src/documentEngine/antiSlopAuditor.js +156 -0
  17. package/src/documentEngine/antiSlopAuditor.ts +221 -0
  18. package/src/documentEngine/documentForge.js +443 -0
  19. package/src/documentEngine/documentForge.ts +519 -0
  20. package/src/documentEngine/screenplayFormatter.js +336 -0
  21. package/src/documentEngine/screenplayFormatter.ts +407 -0
  22. package/src/entrypoint.js +13 -0
  23. package/src/execSafe.js +10 -13
  24. package/src/execSafe.ts +25 -0
  25. package/src/modelRouter.js +56 -123
  26. package/src/modelRouter.ts +134 -0
  27. package/src/observer/advisory.js +164 -0
  28. package/src/observer/advisory.ts +171 -0
  29. package/src/observer/coldObserver.js +462 -0
  30. package/src/observer/coldObserver.ts +540 -0
  31. package/src/observer/evidence.js +435 -0
  32. package/src/observer/evidence.ts +499 -0
  33. package/src/perception.js +303 -118
  34. package/src/perception.ts +387 -0
  35. package/src/procSignal.js +15 -16
  36. package/src/procSignal.ts +31 -0
  37. package/src/promptMentions.js +170 -0
  38. package/src/promptMentions.ts +212 -0
  39. package/src/sandbox.js +1 -1
  40. package/src/textDiff.js +117 -109
  41. package/src/textDiff.ts +169 -0
  42. package/src/toolPacks.js +5 -2
  43. package/src/tools/documentForge.js +215 -0
  44. package/src/tools/documentForge.ts +265 -0
  45. package/src/tools/peerAgent.js +102 -0
  46. package/src/tools/peerAgent.ts +116 -0
  47. package/src/tools/registry.js +3 -1
  48. package/src/tui/completion.js +97 -41
  49. package/src/tui/prompt.js +17 -17
  50. package/src/uncertainty.js +93 -205
  51. package/src/uncertainty.ts +255 -0
  52. package/src/utils/paths.js +15 -18
  53. package/src/utils/paths.ts +36 -0
  54. package/src/vfeTracker.js +122 -181
  55. package/src/vfeTracker.ts +242 -0
@@ -0,0 +1,43 @@
1
+ export interface AgentLoopEvent {
2
+ type: string;
3
+ [key: string]: unknown;
4
+ }
5
+
6
+ export interface AgentLoopEventSink {
7
+ readyState?: number;
8
+ send(payload: string): void;
9
+ }
10
+
11
+ export interface AgentLoopConfig {
12
+ autonomyMode?: 'A' | 'B' | 'C' | string;
13
+ maxStepsPerTask?: number;
14
+ tokenBudget?: Record<string, unknown>;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ export interface ApprovalDecision {
19
+ approved: boolean;
20
+ [key: string]: unknown;
21
+ }
22
+
23
+ export class AgentLoop {
24
+ constructor(eventSink: AgentLoopEventSink | null, taskId: string, userPrompt: string | null);
25
+
26
+ eventSink: AgentLoopEventSink | null;
27
+ taskId: string;
28
+ userPrompt: string | null;
29
+ workspaceRoot: string;
30
+ autonomyOverride: string | null;
31
+ config: AgentLoopConfig;
32
+ stepsCompleted: number;
33
+
34
+ run(): Promise<void>;
35
+ abort(): void;
36
+ rearmAbortController(): void;
37
+ setAutonomyMode(mode: 'A' | 'B' | 'C' | string): void;
38
+ emitEvent(event: AgentLoopEvent): void;
39
+ waitForApproval(toolCallId: string): Promise<boolean>;
40
+ handleApprovalResponse(toolCallId: string, decision: ApprovalDecision | boolean): void;
41
+ waitForDecision(request: Record<string, unknown>): Promise<unknown>;
42
+ updateTierLimits(): void;
43
+ }
package/src/agentLoop.js CHANGED
@@ -22,6 +22,7 @@ import { routedConfig } from './modelRouter.js';
22
22
  import { assessStep, isThrashing } from './uncertainty.js';
23
23
  import { assessDecisionRequirement, isMutatingToolCall, requiresImplementationPlan } from './decisionPolicy.js';
24
24
  import { VFETracker } from './vfeTracker.js';
25
+ import { coldObserver } from './observer/coldObserver.js';
25
26
  import {
26
27
  routePacks,
27
28
  packForTool,
@@ -54,6 +55,7 @@ import os from 'os';
54
55
  import { analyzeFiles, formatFindings, isScannableFile } from './slopDetector.js';
55
56
  import { suggestMovements, formatMovementBrief } from './designPalettes.js';
56
57
  import { createProvider } from './providers/factory.js';
58
+ import { resolveMentionedContext } from './promptMentions.js';
57
59
 
58
60
  const SYSTEM_PROMPT = `You are Causyne Agent, a God-Tier Autonomous AI. You operate the user's computer through a local desktop harness. You are not just a coding assistant; you are a general-purpose superintelligence.
59
61
 
@@ -321,7 +323,9 @@ export class AgentLoop {
321
323
  this.eventSink = eventSink;
322
324
  this.taskId = taskId;
323
325
  this.userPrompt = userPrompt;
324
-
326
+ this.workspaceRoot = process.cwd();
327
+ this.autonomyOverride = null;
328
+
325
329
  this.config = loadConfig();
326
330
  this.sandbox = new Sandbox();
327
331
 
@@ -378,6 +382,7 @@ export class AgentLoop {
378
382
  // prompt-cache prefix intact when a pack loads mid-task.
379
383
  this.unlockOrder = [];
380
384
  this.workspaceContext = null;
385
+ this.activeSubproject = null;
381
386
  this.taskShape = null;
382
387
  // Budget for the billed Tier 2 Cortex condensation pass.
383
388
  this._cortexPassesUsed = 0;
@@ -398,6 +403,7 @@ export class AgentLoop {
398
403
  // P6: rendered surfaces this task wrote, for the anti-slop gate to read back.
399
404
  this.webArtifacts = new Set();
400
405
  this.slopGateFired = false;
406
+ this.coldObserverGuardFired = false;
401
407
 
402
408
  this.gameCascadeState = { active: true, currentStage: 1, stageHistory: [], verificationPassed: false };
403
409
  this.gameToolStats = { godot_act: 0, game_asset_fetch: 0, game_build_html5: 0, last_godot_action: null };
@@ -534,6 +540,15 @@ export class AgentLoop {
534
540
  }
535
541
  }
536
542
 
543
+ setAutonomyMode(mode) {
544
+ const normalized = String(mode || '').trim().toUpperCase();
545
+ if (!['A', 'B', 'C'].includes(normalized)) {
546
+ throw new Error('autonomy mode must be A, B, or C');
547
+ }
548
+ this.autonomyOverride = normalized;
549
+ this.config.autonomyMode = normalized;
550
+ }
551
+
537
552
  /**
538
553
  * A provider for UTILITY work — condensation, classification, filtering.
539
554
  *
@@ -766,6 +781,7 @@ export class AgentLoop {
766
781
 
767
782
  updateTierLimits() {
768
783
  this.config = loadConfig();
784
+ if (this.autonomyOverride) this.config.autonomyMode = this.autonomyOverride;
769
785
  if (typeof this.initializeProvider === 'function') {
770
786
  this.llm = this.initializeProvider();
771
787
  this._utilityLlm = null; // the route is derived from the model; re-derive it
@@ -817,6 +833,8 @@ export class AgentLoop {
817
833
  // awareness instead of burning steps rediscovering it. The result used to be
818
834
  // assigned to a local variable and thrown away.
819
835
  auditLog.log({ event: 'task_start', taskId: this.taskId, request: this.userPrompt });
836
+ this.workspaceRoot = process.cwd();
837
+ coldObserver.beginTask(this.taskId);
820
838
  this.emitEvent({ type: 'status', message: 'Analyzing workspace...' });
821
839
 
822
840
  // P2: open the undo group for this task. This snapshots the CONVERSATION as
@@ -872,6 +890,7 @@ export class AgentLoop {
872
890
  this.webGuardFired = false;
873
891
  this.webArtifacts = new Set();
874
892
  this.slopGateFired = false;
893
+ this.coldObserverGuardFired = false;
875
894
  this.staticContext = null;
876
895
  // Plan cache: the procedure THIS task follows, and the template (if any)
877
896
  // that was offered to it. Both reset per task — a plan is a property of one
@@ -964,6 +983,36 @@ export class AgentLoop {
964
983
  }
965
984
 
966
985
  if (this.userPrompt) {
986
+ // 1b. RESOLVE @ MENTIONS FOR REFERENCED FILES, DIRECTORIES & SUBPROJECTS
987
+ try {
988
+ const subprojects = this.workspaceContext?.subprojects || [];
989
+ const mentionResult = await resolveMentionedContext(this.userPrompt, {
990
+ cwd: process.cwd(),
991
+ subprojects
992
+ });
993
+
994
+ if (mentionResult.detectedSubproject) {
995
+ this.activeSubproject = mentionResult.detectedSubproject;
996
+ this.notice(`Focused on subproject '${mentionResult.detectedSubproject}'`);
997
+ } else if (subprojects.length > 0) {
998
+ const lowerPrompt = this.userPrompt.toLowerCase();
999
+ for (const sp of subprojects) {
1000
+ if (lowerPrompt.includes(sp.toLowerCase())) {
1001
+ this.activeSubproject = sp;
1002
+ this.notice(`Focused on subproject '${sp}'`);
1003
+ break;
1004
+ }
1005
+ }
1006
+ }
1007
+
1008
+ if (mentionResult.referencedContext) {
1009
+ this.userPrompt += mentionResult.referencedContext;
1010
+ this.notice(`Loaded ${mentionResult.mentions.length} referenced @ mention(s) into context`);
1011
+ }
1012
+ } catch (mentionErr) {
1013
+ this.debugLog(`mention resolution error: ${mentionErr.message}`);
1014
+ }
1015
+
967
1016
  this.currentTaskPrompt = this.userPrompt; // Save for notification later
968
1017
 
969
1018
  // V5: Multimodal Attachments Support
@@ -1210,12 +1259,15 @@ export class AgentLoop {
1210
1259
  const ctx = this.workspaceContext;
1211
1260
  const facts = [
1212
1261
  ctx.cwd && `Working directory: ${ctx.cwd}`,
1262
+ this.activeSubproject && `Active project focus: ${this.activeSubproject}`,
1263
+ ctx.projectName && `Workspace project: ${ctx.projectName}`,
1213
1264
  ctx.os && `OS: ${ctx.os}`,
1214
1265
  ctx.framework && `Framework: ${ctx.framework}`,
1215
1266
  ctx.projectType && `Project type: ${ctx.projectType}`,
1216
1267
  ctx.packageManager && `Package manager: ${ctx.packageManager}`,
1217
1268
  ctx.gitBranch && `Git branch: ${ctx.gitBranch}`,
1218
1269
  ctx.gitStatus && `Git status: ${ctx.gitStatus}`,
1270
+ Array.isArray(ctx.subprojects) && ctx.subprojects.length > 0 && `Subprojects in workspace: ${ctx.subprojects.join(', ')}`,
1219
1271
  Array.isArray(ctx.keyFiles) && ctx.keyFiles.length > 0 && `Key files: ${ctx.keyFiles.slice(0, 25).join(', ')}`
1220
1272
  ].filter(Boolean);
1221
1273
  if (facts.length > 0) {
@@ -1640,6 +1692,32 @@ export class AgentLoop {
1640
1692
  }
1641
1693
  }
1642
1694
 
1695
+ // Cold Observer: Overconfidence & Loop Verification Check
1696
+ if (!this.coldObserverGuardFired) {
1697
+ try {
1698
+ const coldAudit = await coldObserver.auditTurnVerification({
1699
+ taskId: this.taskId,
1700
+ userPrompt: this.currentTaskPrompt || this.userPrompt || '',
1701
+ agentClaim: response.content || '',
1702
+ toolHistory: [],
1703
+ stepsCompleted: this.stepsCompleted
1704
+ });
1705
+
1706
+ if (coldAudit.overconfident) {
1707
+ this.coldObserverGuardFired = true;
1708
+ this.emitEvent({ type: 'status', message: `Cold Observer: ${coldAudit.discrepancy}` });
1709
+ this.messages.push({
1710
+ role: 'system',
1711
+ content: coldAudit.coldDirective
1712
+ });
1713
+ this.memoryManager.saveHistory(this.messages);
1714
+ continue;
1715
+ }
1716
+ } catch (_err) {
1717
+ // Cold observer fail-open
1718
+ }
1719
+ }
1720
+
1643
1721
  this.emitEvent({ type: 'status', message: 'Task completed.' });
1644
1722
  this.memoryManager.saveHistory(this.messages); // Save final state
1645
1723
 
@@ -1716,7 +1794,7 @@ export class AgentLoop {
1716
1794
  continue;
1717
1795
  }
1718
1796
 
1719
- this.emitEvent({ type: 'step_start', tool: functionName, args: args });
1797
+ this.emitEvent({ type: 'step_start', tool: functionName, args: args, toolCallId: toolCall.id });
1720
1798
  this.consecutiveCommands++;
1721
1799
 
1722
1800
  if (functionName === 'screenshot' || functionName === 'vision_inspect_ui') {
@@ -1764,7 +1842,7 @@ export class AgentLoop {
1764
1842
 
1765
1843
  if ((['fs_write'].includes(functionName) && isGameFileOrDir) || isEchoingToFile || isCustomScriptBlock) {
1766
1844
  const interceptMsg = `[SOVEREIGN CASCADE VIOLATION: You attempted to skip from Stage ${this.gameCascadeState.currentStage} to Stage 7 without completing Stages 3, 4, 5, and 6 modularly. DO NOT write monolithic 20 KB custom scripts!]`;
1767
- this.emitEvent({ type: 'step_error', tool: functionName, error: '⚡ Sovereign Game Intercept: Cascade violation detected.' });
1845
+ this.emitEvent({ type: 'step_error', tool: functionName, error: 'Sovereign Game Intercept: Cascade violation detected.', toolCallId: toolCall.id });
1768
1846
  this.messages.push({
1769
1847
  role: 'tool',
1770
1848
  tool_call_id: toolCall.id,
@@ -1802,7 +1880,7 @@ export class AgentLoop {
1802
1880
  // Hard Circuit Breaker (Transcendence Context Consciousness)
1803
1881
  if (this.consecutiveBlockedActions >= 4) {
1804
1882
  const fatalMsg = `FATAL ERROR: Infinite Token Drain Detected. Agent repeatedly attempted blocked/forbidden actions 4 times in a row. Forcibly aborting task to save tokens.`;
1805
- this.emitEvent({ type: 'step_error', tool: functionName, error: fatalMsg });
1883
+ this.emitEvent({ type: 'step_error', tool: functionName, error: fatalMsg, toolCallId: toolCall.id });
1806
1884
  auditLog.log({ event: 'task_killed_circuit_breaker', taskId: this.taskId, reason: fatalMsg });
1807
1885
 
1808
1886
  // Save memory of failure for RLTF
@@ -1817,7 +1895,11 @@ export class AgentLoop {
1817
1895
  }
1818
1896
 
1819
1897
  auditLog.log({ event: 'tool_blocked', taskId: this.taskId, tool: functionName, args, reason: validation.reason });
1820
- this.emitEvent({ type: 'step_error', tool: functionName, error: validation.reason });
1898
+ coldObserver.recordToolOutcome(this.taskId, functionName, args, {
1899
+ success: false,
1900
+ error: validation.reason
1901
+ });
1902
+ this.emitEvent({ type: 'step_error', tool: functionName, error: validation.reason, toolCallId: toolCall.id });
1821
1903
 
1822
1904
  // Feed error back to LLM with absolute command to stop repeating
1823
1905
  this.messages.push({
@@ -1896,6 +1978,10 @@ export class AgentLoop {
1896
1978
  event: 'hook_denied', taskId: this.taskId, hookEvent: 'PreToolUse',
1897
1979
  tool: functionName, reason: preHook.reason || 'hook denied'
1898
1980
  });
1981
+ coldObserver.recordToolOutcome(this.taskId, functionName, args, {
1982
+ success: false,
1983
+ error: `PreToolUse hook denied "${functionName}".`
1984
+ });
1899
1985
  this.messages.push({
1900
1986
  role: 'tool',
1901
1987
  tool_call_id: toolCall.id,
@@ -1915,6 +2001,39 @@ export class AgentLoop {
1915
2001
  // downloads ran unannounced.
1916
2002
  const isHeavyAiDownload = functionName === 'manage_local_dependencies' &&
1917
2003
  ['pipInst', 'fetchBin', 'fetchMod'].includes(args.action);
2004
+
2005
+ // Cold Observer: Assess blast radius and mutation risk
2006
+ try {
2007
+ const coldMutation = await coldObserver.evaluateMutation(functionName, args, {
2008
+ workspaceRoot: this.workspaceRoot
2009
+ });
2010
+ if (coldMutation.veto) {
2011
+ this.emitEvent({
2012
+ type: 'status',
2013
+ message: `Cold Observer Veto: Action blocked (${coldMutation.reason})`
2014
+ });
2015
+ coldObserver.recordToolOutcome(this.taskId, functionName, args, {
2016
+ success: false,
2017
+ error: `[COLD OBSERVER VETO]: ${coldMutation.reason}`
2018
+ });
2019
+ this.messages.push({
2020
+ role: 'tool',
2021
+ tool_call_id: toolCall.id,
2022
+ name: functionName,
2023
+ content: JSON.stringify({
2024
+ success: false,
2025
+ error: `[COLD OBSERVER VETO]: ${coldMutation.reason}. Modify your approach to avoid project damage.`
2026
+ })
2027
+ });
2028
+ continue;
2029
+ }
2030
+ if (coldMutation.requiresConfirmation) {
2031
+ validation.risk = 'dangerous';
2032
+ }
2033
+ } catch (_err) {
2034
+ // Cold observer mutation assessment fail-open
2035
+ }
2036
+
1918
2037
  // A standing approval from "Allow always" lifts the gate for this
1919
2038
  // tool until the process restarts. It is checked HERE rather than
1920
2039
 
@@ -1944,6 +2063,10 @@ export class AgentLoop {
1944
2063
  const approved = await this.waitForApproval(toolCall.id);
1945
2064
 
1946
2065
  if (!approved) {
2066
+ coldObserver.recordToolOutcome(this.taskId, functionName, args, {
2067
+ success: false,
2068
+ error: 'User denied this action'
2069
+ });
1947
2070
  this.messages.push({
1948
2071
  role: 'tool',
1949
2072
  tool_call_id: toolCall.id,
@@ -2031,7 +2154,8 @@ export class AgentLoop {
2031
2154
  this.emitEvent({
2032
2155
  type: 'step_error',
2033
2156
  tool: functionName,
2034
- error: `${errorType}: ${toolError.message} (retry ${currentRetries + 1}/${MAX_RETRIES} in ${backoffMs/1000}s)`
2157
+ error: `${errorType}: ${toolError.message} (retry ${currentRetries + 1}/${MAX_RETRIES} in ${backoffMs/1000}s)`,
2158
+ toolCallId: toolCall.id
2035
2159
  });
2036
2160
 
2037
2161
  auditLog.log({
@@ -2066,7 +2190,12 @@ export class AgentLoop {
2066
2190
  this.emitEvent({
2067
2191
  type: 'step_error',
2068
2192
  tool: functionName,
2069
- error: `Failed after ${MAX_RETRIES} attempts: ${toolError.message}`
2193
+ error: `Failed after ${MAX_RETRIES} attempts: ${toolError.message}`,
2194
+ toolCallId: toolCall.id
2195
+ });
2196
+ coldObserver.recordToolOutcome(this.taskId, functionName, args, {
2197
+ success: false,
2198
+ error: `PERMANENT FAILURE after ${MAX_RETRIES} retries: ${toolError.message}`
2070
2199
  });
2071
2200
 
2072
2201
  this.messages.push({
@@ -2104,6 +2233,11 @@ export class AgentLoop {
2104
2233
  };
2105
2234
  }
2106
2235
 
2236
+ // The journal is the observer's own record of what actually ran. It
2237
+ // is written only after post-hook review, so a formatter that rejects
2238
+ // a write is remembered as a failure rather than a success.
2239
+ coldObserver.recordToolOutcome(this.taskId, functionName, args, result);
2240
+
2107
2241
  // PROGRESSIVE DISCLOSURE: honour unlock signals from tool_search.
2108
2242
  // The control fields are stripped before the result reaches the LLM.
2109
2243
  if (result && (result._unlock_tools || result._unlock_packs)) {
@@ -2121,7 +2255,7 @@ export class AgentLoop {
2121
2255
  // Buffer output for disconnect resilience
2122
2256
  await this.taskBuffer.appendOutput(this.taskId, { tool: functionName, result });
2123
2257
 
2124
- this.emitEvent({ type: 'step_complete', tool: functionName, result: result._is_image_marker ? '[IMAGE DATA]' : result });
2258
+ this.emitEvent({ type: 'step_complete', tool: functionName, result: result._is_image_marker ? '[IMAGE DATA]' : result, toolCallId: toolCall.id });
2125
2259
  this.stepsCompleted++;
2126
2260
 
2127
2261
  // The procedure, as it actually ran. Tool name plus a whitelisted
package/src/commands.js CHANGED
@@ -83,6 +83,56 @@ export const COMMANDS = [
83
83
  }
84
84
  },
85
85
 
86
+ {
87
+ name: 'cd',
88
+ aliases: ['cwd', 'workspace'],
89
+ scope: 'local',
90
+ usage: '/cd [directory]',
91
+ description: 'View or change active working directory and project focus',
92
+ complete: () => {
93
+ try {
94
+ const entries = fs.readdirSync(process.cwd(), { withFileTypes: true });
95
+ return entries
96
+ .filter(e => e.isDirectory() && !e.name.startsWith('.'))
97
+ .map(e => ({
98
+ value: e.name.includes(' ') ? `"${e.name}"` : e.name,
99
+ label: e.name + '/',
100
+ description: 'directory'
101
+ }));
102
+ } catch (e) {
103
+ return [];
104
+ }
105
+ },
106
+ run: async (argv, ctx) => {
107
+ const target = argv.join(' ').trim().replace(/^["']|["']$/g, '');
108
+ if (!target) {
109
+ return ok(`Current working directory: ${process.cwd()}`);
110
+ }
111
+ const resolved = path.resolve(process.cwd(), target.replace(/^~\//, os.homedir() + '/'));
112
+ if (!fs.existsSync(resolved)) {
113
+ return fail(`Directory not found: ${resolved}`);
114
+ }
115
+ const stat = fs.statSync(resolved);
116
+ if (!stat.isDirectory()) {
117
+ return fail(`Not a directory: ${resolved}`);
118
+ }
119
+ try {
120
+ process.chdir(resolved);
121
+ } catch (err) {
122
+ return fail(`Cannot switch to ${resolved}: ${err.message}`);
123
+ }
124
+ if (ctx.prompt && typeof ctx.prompt.setWorkingRoot === 'function') {
125
+ ctx.prompt.setWorkingRoot(resolved);
126
+ }
127
+ if (ctx.loop && ctx.loop.perception) {
128
+ try {
129
+ ctx.loop.workspaceContext = await ctx.loop.perception.buildContext(resolved);
130
+ } catch (e) {}
131
+ }
132
+ return ok(`Working directory changed to: ${resolved}`);
133
+ }
134
+ },
135
+
86
136
  {
87
137
  name: 'skill',
88
138
  scope: 'local',
@@ -878,6 +928,159 @@ export const COMMANDS = [
878
928
  run: (argv, ctx) => ok(buildStatusText({ loop: ctx.loop || null }))
879
929
  },
880
930
 
931
+ {
932
+ name: 'observer',
933
+ scope: 'local',
934
+ usage: '/observer [status|toggle|mode <deterministic|advisory>|endpoint <url|none>|model <name>|strictness <relaxed|standard|strict>|test|audit|reset]',
935
+ description: 'Inspect or configure the Cold Observer',
936
+ dialogTitle: 'Cold Observer',
937
+ run: async (argv) => {
938
+ const { coldObserver } = await import('./observer/coldObserver.js');
939
+ const sub = (argv[0] || 'status').toLowerCase();
940
+
941
+ if (sub === 'toggle') {
942
+ const current = coldObserver.getConfig();
943
+ coldObserver.updateConfig({ enabled: !current.enabled });
944
+ return ok(`Cold Observer is now ${!current.enabled ? 'ENABLED' : 'DISABLED'}.`);
945
+ }
946
+
947
+ if (sub === 'mode') {
948
+ const targetMode = String(argv[1] || '').toLowerCase();
949
+ if (!['deterministic', 'advisory', 'local', 'hybrid'].includes(targetMode)) {
950
+ return fail('Invalid mode. Choose: deterministic or advisory.');
951
+ }
952
+ coldObserver.updateConfig({ mode: targetMode });
953
+ return ok(`Cold Observer mode set to "${coldObserver.getConfig().mode}".`);
954
+ }
955
+
956
+ if (sub === 'endpoint') {
957
+ const target = String(argv[1] || '').trim();
958
+ try {
959
+ coldObserver.updateConfig({ endpoint: target === 'none' ? '' : target });
960
+ } catch (error) {
961
+ return fail(error instanceof Error ? error.message : 'Invalid endpoint.');
962
+ }
963
+ const endpoint = coldObserver.getConfig().endpoint;
964
+ return ok(endpoint ? `Advisory endpoint set to ${endpoint}.` : 'Advisory endpoint cleared.');
965
+ }
966
+
967
+ if (sub === 'model') {
968
+ const target = String(argv[1] || '').trim();
969
+ coldObserver.updateConfig({ model: target === 'none' ? '' : target });
970
+ const model = coldObserver.getConfig().model;
971
+ return ok(model ? `Advisory model set to ${model}.` : 'Advisory model cleared.');
972
+ }
973
+
974
+ if (sub === 'strictness') {
975
+ const target = String(argv[1] || '').toLowerCase();
976
+ if (!['relaxed', 'standard', 'strict', 'moderate', 'paranoid'].includes(target)) {
977
+ return fail('Invalid strictness. Choose: relaxed, standard, or strict.');
978
+ }
979
+ coldObserver.updateConfig({ strictness: target });
980
+ return ok(`Cold Observer strictness set to "${coldObserver.getConfig().strictness}".`);
981
+ }
982
+
983
+ if (sub === 'test') {
984
+ const health = await coldObserver.healthCheck();
985
+ if (!health.endpoint) {
986
+ return ok('No advisory endpoint configured. The deterministic observer needs no endpoint and remains active.');
987
+ }
988
+ return ok([
989
+ `Endpoint: ${health.endpoint}`,
990
+ `Model: ${health.model || '(unset)'}`,
991
+ `Healthy: ${health.healthy ? 'yes' : 'no'} (${health.latencyMs}ms)`,
992
+ `Detail: ${health.detail}`
993
+ ].join('\n'));
994
+ }
995
+
996
+ if (sub === 'reset') {
997
+ coldObserver.updateConfig({
998
+ enabled: true,
999
+ mode: 'deterministic',
1000
+ endpoint: '',
1001
+ model: '',
1002
+ timeoutMs: 800,
1003
+ strictness: 'standard'
1004
+ });
1005
+ return ok('Cold Observer configuration reset to defaults.');
1006
+ }
1007
+
1008
+ if (sub === 'audit') {
1009
+ const audits = coldObserver.getAuditHistory();
1010
+ if (audits.length === 0) {
1011
+ return ok('No Cold Observer audit records yet.');
1012
+ }
1013
+ const lines = audits.slice(-10).map((a) => {
1014
+ const time = new Date(a.timestamp).toLocaleTimeString();
1015
+ const tag = a.type === 'mutation' ? `[mutation ${a.tool || ''}]` : '[verification]';
1016
+ const score = typeof a.riskScore === 'number' ? `(risk ${(a.riskScore * 100).toFixed(0)}%)` : '';
1017
+ const veto = a.veto ? 'VETO' : 'pass';
1018
+ return `${time} ${tag} ${veto} ${score} ${a.details}`;
1019
+ });
1020
+ return ok(`Recent Cold Observer audits (last 10):\n\n${lines.join('\n')}`);
1021
+ }
1022
+
1023
+ const cfg = coldObserver.getConfig();
1024
+ const audits = coldObserver.getAuditHistory();
1025
+ const vetoes = audits.filter((a) => a.veto).length;
1026
+ return ok([
1027
+ 'Cold Observer',
1028
+ '──────────────────────────────────────────────',
1029
+ `Status: ${cfg.enabled ? 'Active (mutations and completion claims)' : 'Disabled'}`,
1030
+ `Mode: ${cfg.mode.toUpperCase()}`,
1031
+ `Strictness: ${cfg.strictness}`,
1032
+ `Advisory: ${cfg.endpoint ? `${cfg.model || '(model unset)'} at ${cfg.endpoint}` : 'not configured (deterministic only)'}`,
1033
+ `Audit events: ${audits.length} recorded (${vetoes} vetoes enforced)`,
1034
+ '',
1035
+ 'Subcommands:',
1036
+ ' /observer toggle Enable or disable protection',
1037
+ ' /observer mode <deterministic|advisory> Choose the review mode',
1038
+ ' /observer endpoint <url|none> Set a loopback advisory endpoint',
1039
+ ' /observer model <name> Set the advisory model name',
1040
+ ' /observer strictness <relaxed|standard|strict>',
1041
+ ' /observer test Probe the advisory endpoint',
1042
+ ' /observer audit Inspect recent audits',
1043
+ ' /observer reset Restore defaults'
1044
+ ].join('\n'));
1045
+ }
1046
+ },
1047
+
1048
+ {
1049
+ name: 'acp',
1050
+ scope: 'local',
1051
+ usage: '/acp [status|peers]',
1052
+ description: 'Agent Client Protocol (ACP) server and peer delegation status',
1053
+ dialogTitle: 'Agent Client Protocol',
1054
+ run: async (argv) => {
1055
+ const sub = (argv[0] || 'status').toLowerCase();
1056
+ if (sub === 'peer' || sub === 'peers') {
1057
+ const { loadPeerProfiles, peerProfilesPath } = await import('./acp/peerProfiles.js');
1058
+ const profiles = loadPeerProfiles();
1059
+ return ok([
1060
+ 'Peer delegation (ACP)',
1061
+ '──────────────────────────────────────────────',
1062
+ `Profiles file: ${peerProfilesPath()}`,
1063
+ `Configured: ${profiles.length === 0 ? 'none' : profiles.map((p) => `${p.id} (${p.policy})`).join(', ')}`,
1064
+ '',
1065
+ 'The model selects a profile id and writes a prompt. Executable, arguments,',
1066
+ 'working directory, and environment always come from the profile file.'
1067
+ ].join('\n'));
1068
+ }
1069
+ return ok([
1070
+ 'Agent Client Protocol (ACP) - Causyne Agent',
1071
+ '──────────────────────────────────────────────',
1072
+ 'Version: ACP v1 (JSON-RPC 2.0 over stdio)',
1073
+ 'Server command: causyne acp',
1074
+ 'Wire: newline-delimited JSON-RPC',
1075
+ 'Sessions: session/new, session/prompt, session/cancel, session/update',
1076
+ 'Optional: session/list, session/close, session/set_mode',
1077
+ 'Workspace: one workspace per server process',
1078
+ '',
1079
+ 'Run "causyne acp" to connect this agent to any ACP client.'
1080
+ ].join('\n'));
1081
+ }
1082
+ },
1083
+
881
1084
  {
882
1085
  name: 'exit',
883
1086
  aliases: ['quit'],
@@ -329,6 +329,16 @@ export const COMPILED_SKILLS = {
329
329
  "description": "Automate photo editing, graphic composition, text typography, and image format exports using native Photo Studio automation. Use when a task requires batch image edits, graphic layout creation, resizing, color adjustment, or PSD/PNG/JPG/WEBP asset generation.",
330
330
  "content": "---\nname: photo-studio-automation\ndescription: Automate photo editing, graphic composition, text typography, and image format exports using native Photo Studio automation. Use when a task requires batch image edits, graphic layout creation, resizing, color adjustment, or PSD/PNG/JPG/WEBP asset generation.\n---\n\n# Photo Studio Automation\n\nDirect script execution beats screenshot clicking every time: it is exact, sub-second, costs zero vision tokens, and never misclicks because a window moved. Use the native Creative Studio bridge via `creative_apps` with `action: \"studio_edit\"` and `host: \"photo-studio\"`.\n\n## Workflow\n\n1. Verify environment and inspect installed creative hosts.\n - Run `creative_apps` with `action: \"status\"`.\n - Confirm Photo Studio availability before assembling jobs.\n - **Done when:** host availability is confirmed.\n\n2. Establish workspace paths.\n - All inputs and outputs must reside strictly within the active workspace.\n - The output file must not already exist (the runtime enforces collision prevention).\n - Supported output extensions: `.psd`, `.png`, `.jpg`, `.jpeg`, `.webp`.\n - **Done when:** source asset and clean target output paths are defined.\n\n3. Plan layer hierarchy and typography before applying edits.\n - If starting from scratch, omit `input` to let the engine create a canvas (1920x1080 standard).\n - If transforming existing art, provide `input` path.\n - Layer modifications should specify explicit `name`, `opacity`, `visible`, and `blendMode`.\n - Typography actions should define `text`, `x`, `y`, `size`, and font family.\n - **Done when:** sequence of edit actions is organized logically from background to foreground.\n\n4. Apply color adjustments and canvas sizing.\n - Use `resize` or `crop` early in the action pipeline to establish composition geometry.\n - Use `adjust_color` with `brightness` (-100..100) and `contrast` (-100..100) on target layers for grading.\n - If delivering flat web assets (e.g., PNG or WEBP), include `flatten` at the end of the action chain.\n - **Done when:** geometric and tonal parameters match design requirements.\n\n5. Execute batch and verify output.\n - Invoke `creative_apps` with `action: \"studio_edit\"`, `host: \"photo-studio\"`, `output`, and `actions`.\n - The runtime verifies execution receipt and confirms nonempty file generation.\n - **Done when:** output file exists, has nonzero byte count, and matches requested specifications.\n\n## Failure Modes Worth Naming\n\n- **Overwriting existing file:** The engine halts if output exists. Specify a distinct or versioned filename.\n- **Outside workspace:** All paths must resolve inside the workspace root. Do not reference temporary directories or home root.\n- **Missing host:** If the host is not installed, the tool gracefully reports installation status. Propose alternative image processing or check system paths.\n- **Out-of-range layer index:** Layer indices start at 0. Ensure index matches existing layer count.\n\n## Verification\n\n- `action: \"studio_edit\"` returns `success: true`.\n- Output file exists on disk within workspace.\n- File size is non-zero.\n- Format matches target extension (.psd, .png, .jpg, or .webp).\n"
331
331
  },
332
+ "professional-document-publishing": {
333
+ "name": "professional-document-publishing",
334
+ "description": "Generate publication-grade PDF documents, grant proposals, financial sheets, and technical reports using HTML/CSS Paged Media and Headless Chrome. Enforces visual quality inspection, under 2MB compliance, and mathematical integrity.",
335
+ "content": "---\nname: professional-document-publishing\ndescription: Generate publication-grade PDF documents, grant proposals, financial sheets, and technical reports using HTML/CSS Paged Media and Headless Chrome. Enforces visual quality inspection, under 2MB compliance, and mathematical integrity.\n---\n\n# Professional Document Publishing & Harness\n\nFrontier LLMs and efficient models produce equally stunning, publication-ready documents when guided by this harness methodology.\n\nNever assemble print layouts through raw Python scripts. Always author in semantic HTML5 plus CSS Paged Media, compile via Headless Chrome, and visually inspect the rendered output.\n\n## Workflow\n\n1. Prepare structured data and schemas.\n - Keep numeric budget and scheduling data in structured JSON format.\n - Run math validation with `document_forge` (`action: \"audit_budget\"`).\n - **Done when:** all numbers are validated against the single source of truth.\n\n2. Compile document markup and styles.\n - Author page layouts in HTML using standard CSS `@page` rules (`size: A4 portrait; margin: 20mm 18mm 18mm 18mm;`).\n - Use `@page cover { margin: 0; }` for full-bleed cover banners without white borders.\n - Force `-webkit-print-color-adjust: exact; print-color-adjust: exact;` so backgrounds and gradients render faithfully.\n - Prevent mid-table row cuts with `tr { page-break-inside: avoid; }`.\n - Prevent orphan headers with `h1, h2, h3 { page-break-after: avoid; }`.\n - Compile via `document_forge` (`action: \"compile_document\"`).\n - **Done when:** HTML markup and CSS paged styles are written to disk.\n\n3. Render to vector PDF via Headless Chrome.\n - Invoke `document_forge` (`action: \"render_pdf\"`, `sourceHtmlPath`, `outputPdfPath`).\n - **Done when:** vector PDF file exists on disk with non-zero size.\n\n4. Inspect pages visually and audit compliance.\n - Extract page images via `document_forge` (`action: \"inspect_pages\"`).\n - Verify page fill density: flag and repair any pages with less than 25% text coverage.\n - Confirm PDF file size is below 2,048 KB for portal compliance.\n - Audit text content for long em-dashes and AI clichés with `document_forge` (`action: \"audit_text\"`).\n - **Done when:** all pages pass visual density check and size constraints.\n\n## Verification\n\n- `document_forge` returns `success: true`.\n- Output PDF file size is non-zero and under 2,048 KB.\n- Extracted page images show no clipped text or orphaned headers.\n- Mathematical totals match across all sections.\n"
336
+ },
337
+ "screenplay-and-shotlist-forge": {
338
+ "name": "screenplay-and-shotlist-forge",
339
+ "description": "Format professional Hollywood screenplays in Courier 12pt, create director production shot lists, and build voiceover timing cue sheets with Words-Per-Minute pacing analysis. Use for movie scripts, video storytelling, documentary shotlists, and audio dubbing timelines.",
340
+ "content": "---\nname: screenplay-and-shotlist-forge\ndescription: Format professional Hollywood screenplays in Courier 12pt, create director production shot lists, and build voiceover timing cue sheets with Words-Per-Minute pacing analysis. Use for movie scripts, video storytelling, documentary shotlists, and audio dubbing timelines.\n---\n\n# Screenplay & Production Shot List Forge\n\nHigh-end cinematic storytelling and video production require standardized industry formatting so that directors, cinematographers, and editors can immediately execute on set and in the edit suite.\n\n## Specifications\n\n- **Screenplay Typography:** Courier 12pt monospaced font, exactly 6 lines per inch vertical rhythm.\n- **Screenplay Margins:** Left margin 1.5 inches; right, top, and bottom margins 1.0 inch.\n- **Pacing Standard:** Voiceover narration moves at 130 to 155 Words-Per-Minute for natural documentary cadence.\n\n## Workflow\n\n1. Format screenplay elements and layout.\n - Author scenes using `document_forge` (`action: \"format_screenplay\"`, `screenplaySpec`).\n - Format scene headings in uppercase, character cues indented 2.2 inches, and dialogue width constrained to 3.5 inches.\n - **Done when:** script HTML and CSS with Courier 12pt specifications are generated.\n\n2. Build director's production shot list.\n - Generate production shot lists with `document_forge` (`action: \"format_shotlist\"`, `shotList`).\n - Assign camera framing (EWS, WS, MS, CU, ECU), lenses, movement, and audio cues.\n - **Done when:** structured shot list grid is compiled with cumulative duration estimates.\n\n3. Analyze voiceover timing and narration pacing.\n - Audit narration pacing with `document_forge` (`action: \"analyze_vo\"`, `voBlocks`).\n - Ensure word density stays within 130 to 155 WPM for optimal documentary pacing.\n - **Done when:** pacing status is confirmed as optimal across all audio blocks.\n\n4. Render and verify publication files.\n - Render scripts or shot lists to vector PDF via `document_forge` (`action: \"render_pdf\"`).\n - **Done when:** output PDF files exist and are verified visually.\n\n## Verification\n\n- `document_forge` returns `success: true`.\n- Screenplay HTML contains Courier 12pt typography and 1.5-inch left margin.\n- Shot list table summarizes total shots and total runtime.\n- Voiceover analysis confirms pacing within target WPM limits.\n"
341
+ },
332
342
  "video-timeline-assembly": {
333
343
  "name": "video-timeline-assembly",
334
344
  "description": "Assemble video timelines, import media clips to sequences, place markers, make precision edits, and export project files using native Video Studio automation. Use when a task requires automated video sequence building, cutting, multicam timeline layout, or PRPROJ/XML project generation.",