fraim-framework 2.0.146 → 2.0.147

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.
@@ -253,22 +253,40 @@ function detectEmployees() {
253
253
  };
254
254
  });
255
255
  }
256
- // Rewrite a /fraim or $fraim job invocation to a direct MCP tool call instruction.
257
- // Gemini CLI has a /fraim command but it conflicts when both workspace-level and
258
- // user-level fraim.toml commands exist (Gemini renames both), making /fraim
259
- // unrecognised. Sending an explicit get_fraim_job instruction bypasses the slash
260
- // command entirely and works regardless of whether the command exists.
261
- function transformGeminiMessage(message) {
262
- const match = message.match(/^[/$]fraim\s+(\S+)\n?([\s\S]*)$/);
256
+ function parseFraimInvocation(message) {
257
+ const trimmed = message.trim();
258
+ if (!trimmed)
259
+ return null;
260
+ const match = trimmed.match(/^[/$]fraim(?:\s+(\S+))?\s*([\s\S]*)$/);
263
261
  if (!match)
262
+ return null;
263
+ return {
264
+ jobId: match[1] || null,
265
+ remainder: (match[2] || '').trim(),
266
+ };
267
+ }
268
+ // Rewrite UI-facing /fraim or $fraim invocations into direct MCP tool
269
+ // instructions for headless hosts. The Hub still shows the familiar
270
+ // invocation in the manager timeline, but the actual CLI child process
271
+ // receives an explicit instruction that works in non-interactive mode.
272
+ function transformHeadlessFraimMessage(message, kind) {
273
+ const parsed = parseFraimInvocation(message);
274
+ if (!parsed)
275
+ return message;
276
+ if (kind === 'continue') {
277
+ if (parsed.remainder) {
278
+ return `Continue the active FRAIM job with this manager coaching:\n\n${parsed.remainder}`;
279
+ }
280
+ return 'Continue the active FRAIM job using the current session context.';
281
+ }
282
+ if (!parsed.jobId)
264
283
  return message;
265
- const jobId = match[1];
266
- const instructions = match[2].trim();
267
284
  const parts = [
268
- `Call the get_fraim_job MCP tool with job "${jobId}" to get the full job instructions, then follow them exactly.`,
285
+ `Call the get_fraim_job MCP tool with job "${parsed.jobId}" to get the full job instructions, then follow them exactly.`,
269
286
  ];
270
- if (instructions)
271
- parts.push(`\n\nUser instructions: ${instructions}`);
287
+ if (parsed.remainder) {
288
+ parts.push(`\n\nUser instructions: ${parsed.remainder}`);
289
+ }
272
290
  return parts.join('');
273
291
  }
274
292
  // If ~/.gemini/settings.json has a wrong/test FRAIM_API_KEY, patch it with the
@@ -301,7 +319,7 @@ function buildStartPlan(hostId, message) {
301
319
  return {
302
320
  command: executableName('codex'),
303
321
  args: ['exec', '--json', '--skip-git-repo-check', '--dangerously-bypass-approvals-and-sandbox'],
304
- stdin: message,
322
+ stdin: transformHeadlessFraimMessage(message, 'start'),
305
323
  };
306
324
  }
307
325
  if (hostId === 'gemini') {
@@ -309,13 +327,13 @@ function buildStartPlan(hostId, message) {
309
327
  return {
310
328
  command: executableName('gemini'),
311
329
  args: ['--yolo', '--skip-trust'],
312
- stdin: transformGeminiMessage(message),
330
+ stdin: transformHeadlessFraimMessage(message, 'start'),
313
331
  };
314
332
  }
315
333
  return {
316
334
  command: executableName('claude'),
317
335
  args: ['-p', '--verbose', '--output-format', 'stream-json', '--dangerously-skip-permissions'],
318
- stdin: message,
336
+ stdin: transformHeadlessFraimMessage(message, 'start'),
319
337
  };
320
338
  }
321
339
  function buildContinuePlan(hostId, sessionId, message) {
@@ -323,7 +341,7 @@ function buildContinuePlan(hostId, sessionId, message) {
323
341
  return {
324
342
  command: executableName('codex'),
325
343
  args: ['exec', 'resume', '--json', '--skip-git-repo-check', '--dangerously-bypass-approvals-and-sandbox', sessionId],
326
- stdin: message,
344
+ stdin: transformHeadlessFraimMessage(message, 'continue'),
327
345
  };
328
346
  }
329
347
  if (hostId === 'gemini') {
@@ -332,13 +350,13 @@ function buildContinuePlan(hostId, sessionId, message) {
332
350
  return {
333
351
  command: executableName('gemini'),
334
352
  args: ['--yolo', '--skip-trust'],
335
- stdin: message,
353
+ stdin: transformHeadlessFraimMessage(message, 'continue'),
336
354
  };
337
355
  }
338
356
  return {
339
357
  command: executableName('claude'),
340
358
  args: ['-p', '--verbose', '--output-format', 'stream-json', '--dangerously-skip-permissions', '-r', sessionId],
341
- stdin: message,
359
+ stdin: transformHeadlessFraimMessage(message, 'continue'),
342
360
  };
343
361
  }
344
362
  function parseHostLine(hostId, line) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.146",
3
+ "version": "2.0.147",
4
4
  "description": "FRAIM: AI Workforce Infrastructure — the organizational capability that turns AI agents into an accountable workforce, their operators into capable AI managers, and executives into leaders with clear optics on AI proficiency.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -11,13 +11,16 @@
11
11
 
12
12
  <div class="page">
13
13
 
14
- <header class="header">
15
- <h1>AI Hub</h1>
16
- <button class="project-button" type="button" id="project-button">
17
- <span class="folder">Project</span>
18
- <strong id="project-name">Choose a folder</strong>
19
- </button>
20
- </header>
14
+ <header class="header">
15
+ <div class="header-copy">
16
+ <span class="header-eyebrow">FRAIM Hub</span>
17
+ <h1>AI Hub</h1>
18
+ </div>
19
+ <button class="project-button" type="button" id="project-button">
20
+ <span class="folder">Project</span>
21
+ <strong id="project-name">Choose a folder</strong>
22
+ </button>
23
+ </header>
21
24
 
22
25
  <section class="welcome">
23
26
  Hi <strong class="you">there</strong>, remember, you are the
@@ -63,13 +66,20 @@
63
66
  </section>
64
67
 
65
68
  <div class="layout">
66
- <aside class="rail">
67
- <button class="new-conv" type="button" id="new-conv-btn">+ New job</button>
68
- <!-- R2.4: team roster horizontal row of avatar chips per hired persona -->
69
- <div class="team-roster" id="team-roster" hidden></div>
70
-
71
- <div class="conv-list" id="conv-list"></div>
72
- </aside>
69
+ <aside class="rail">
70
+ <button class="new-conv" type="button" id="new-conv-btn">+ New job</button>
71
+ <div class="rail-note">Alpha: browser shell for directing employees across your project.</div>
72
+ <!-- R2.4: team roster — horizontal row of avatar chips per hired persona -->
73
+ <section class="rail-section rail-section--employees">
74
+ <div class="rail-section-label">Hired employees</div>
75
+ <div class="team-roster" id="team-roster" hidden></div>
76
+ </section>
77
+
78
+ <section class="rail-section rail-section--runs">
79
+ <div class="rail-section-label">Runs</div>
80
+ <div class="conv-list" id="conv-list"></div>
81
+ </section>
82
+ </aside>
73
83
 
74
84
  <main class="conversation" id="conversation">
75
85
  <div class="empty-state" id="empty">
@@ -77,16 +87,25 @@
77
87
  <p>Pick an existing job from the left, or click <strong>+ New job</strong> to give your employee something to work on.</p>
78
88
  </div>
79
89
 
80
- <div id="active-conv" hidden>
81
- <div class="conv-header">
82
- <h2 id="active-title"></h2>
83
- <div class="conv-job" id="active-job"></div>
84
- <div id="artifact-slot"></div>
85
- </div>
86
-
87
- <!-- Issue #347 R1: pizza tracker. Hidden when the active job
88
- declares no phases. Populated by renderTracker() in script.js. -->
89
- <div class="tracker" id="tracker" aria-label="Job progress" hidden>
90
+ <div id="active-conv" hidden>
91
+ <div class="conv-topline">
92
+ <div class="employee-identity" id="active-identity"></div>
93
+ <div class="run-state-pill" id="run-state-pill"></div>
94
+ </div>
95
+
96
+ <div class="conv-header">
97
+ <div class="title-block">
98
+ <h2 id="active-title"></h2>
99
+ <div class="conv-job" id="active-job"></div>
100
+ </div>
101
+ <div id="artifact-slot"></div>
102
+ </div>
103
+
104
+ <div class="summary-strip" id="summary-strip"></div>
105
+
106
+ <!-- Issue #347 R1: pizza tracker. Hidden when the active job
107
+ declares no phases. Populated by renderTracker() in script.js. -->
108
+ <div class="tracker" id="tracker" aria-label="Job progress" hidden>
90
109
  <div class="tracker-rows" id="tracker-rows"></div>
91
110
  <div class="tracker-note" id="tracker-note" hidden></div>
92
111
  </div>
@@ -96,28 +115,32 @@
96
115
  <span class="latest" id="latest"></span>
97
116
  </div>
98
117
 
99
- <div class="messages" id="messages"></div>
118
+ <section class="thread-surface" aria-label="Manager and employee thread">
119
+ <div class="thread-surface-label">Manager and employee thread</div>
120
+ <div class="messages" id="messages"></div>
121
+ </section>
100
122
 
101
123
  <div class="coach">
102
- <div class="coach-title-row">
103
- <span class="section-title">Coach the employee</span>
104
- <span class="active-employee-row">
105
- <label for="active-employee-select" class="active-employee-label">via</label>
106
- <select id="active-employee-select" class="employee-select inline"></select>
107
- </span>
108
- </div>
109
- <textarea id="coach-text" placeholder="Tell the employee what to do next…"></textarea>
110
- <div class="coach-actions">
111
- <!-- Issue #347 R2: template picker. Hidden when the project
112
- has no manager-job templates. -->
113
- <button class="ghost" type="button" id="template-picker-btn" aria-haspopup="menu" aria-expanded="false" hidden>Use a template ▾</button>
114
- <button class="send-button" type="button" id="send" disabled>Send</button>
115
- <div class="template-popover" id="template-popover" role="menu" hidden></div>
116
- </div>
117
- <!-- Issue #347 R4: run-level totals. Discoverable, not dominating.
118
- Populated by renderTotals() each poll tick. -->
119
- <div class="totals" id="totals" aria-label="Run totals" hidden></div>
120
- </div>
124
+ <div class="coach-title-row">
125
+ <span class="section-title">Coach the employee</span>
126
+ <span class="active-employee-row">
127
+ <label for="active-employee-select" class="active-employee-label">tool</label>
128
+ <select id="active-employee-select" class="employee-select inline"></select>
129
+ </span>
130
+ </div>
131
+ <textarea id="coach-text" placeholder="Tell the employee what to do next…"></textarea>
132
+ <div class="coach-actions">
133
+ <!-- Issue #347 R2: template picker. Hidden when the project
134
+ has no manager-job templates. -->
135
+ <button class="ghost" type="button" id="template-picker-btn" aria-haspopup="menu" aria-expanded="false" hidden>Use a template ▾</button>
136
+ <button class="send-button" type="button" id="send" disabled>Send</button>
137
+ <div class="template-popover" id="template-popover" role="menu" hidden></div>
138
+ </div>
139
+ <div class="coach-note" id="coach-note"></div>
140
+ <!-- Issue #347 R4: run-level totals. Discoverable, not dominating.
141
+ Populated by renderTotals() each poll tick. -->
142
+ <div class="totals" id="totals" aria-label="Run totals" hidden></div>
143
+ </div>
121
144
 
122
145
  <details class="micro" id="micro-manage">
123
146
  <summary>Micro-manage — raw host details</summary>