create-pmos 0.8.0 → 0.9.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/README.md +24 -0
- package/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/skills/pmos-router.skill +22 -0
- package/payload/skills-native/pmos-router/SKILL.md +22 -0
- package/workspace/README.md +28 -0
- package/workspace/lib/console-state.js +56 -1
- package/workspace/lib/git-info.js +197 -25
- package/workspace/lib/session-state.js +139 -0
- package/workspace/lib/transcript-read.js +28 -6
- package/workspace/public/app.js +314 -58
- package/workspace/public/index.html +24 -3
- package/workspace/public/styles.css +875 -572
- package/workspace/public/vendor/ibm-plex-mono-400.woff2 +0 -0
- package/workspace/public/vendor/ibm-plex-mono-700.woff2 +0 -0
- package/workspace/public/vendor/space-grotesk-var.woff2 +0 -0
- package/workspace/server.js +338 -39
|
@@ -168,7 +168,7 @@ function prettyTool(name) {
|
|
|
168
168
|
*/
|
|
169
169
|
function readHarness(file, clean = (x) => x) {
|
|
170
170
|
const out = { model: null, effort: null, turns: 0, tokens: null, tools: null, skills: null,
|
|
171
|
-
title: null, openCall: null, missing: {} };
|
|
171
|
+
title: null, openCall: null, turn: null, missing: {} };
|
|
172
172
|
let raw;
|
|
173
173
|
try {
|
|
174
174
|
const st = fs.statSync(file);
|
|
@@ -199,6 +199,11 @@ function readHarness(file, clean = (x) => x) {
|
|
|
199
199
|
// report the raw fact {verb, sinceTs}; we never claim it is live.
|
|
200
200
|
const openUse = new Map(); // tool_use_id → { verb, ts } (insertion order = chronological)
|
|
201
201
|
const resolved = new Set(); // tool_use_ids that got a tool_result
|
|
202
|
+
// WHOSE TURN IS IT (workspace-waiting-on-you R1). The last TURN-TAKING record's role — nothing
|
|
203
|
+
// more. `ai-title` and `attachment` records are CLI bookkeeping and are deliberately not
|
|
204
|
+
// turn-taking, and neither is a SYNTHETIC assistant record: counting one as "the assistant
|
|
205
|
+
// finished speaking" is exactly how a false `needs you` would be manufactured.
|
|
206
|
+
let lastRole = null; // 'assistant' | 'user' | null
|
|
202
207
|
|
|
203
208
|
for (const line of raw.split('\n')) {
|
|
204
209
|
if (!line) continue;
|
|
@@ -217,6 +222,7 @@ function readHarness(file, clean = (x) => x) {
|
|
|
217
222
|
for (const c of (Array.isArray(um.content) ? um.content : [])) {
|
|
218
223
|
if (c && c.type === 'tool_result' && c.tool_use_id) resolved.add(c.tool_use_id);
|
|
219
224
|
}
|
|
225
|
+
lastRole = 'user'; // a prompt was submitted, or a tool result landed
|
|
220
226
|
continue;
|
|
221
227
|
}
|
|
222
228
|
if (r.type === 'attachment') {
|
|
@@ -234,6 +240,7 @@ function readHarness(file, clean = (x) => x) {
|
|
|
234
240
|
if (m.model && m.model !== SYNTHETIC) {
|
|
235
241
|
out.model = clean(m.model);
|
|
236
242
|
out.turns++; // only real model turns count
|
|
243
|
+
lastRole = 'assistant'; // ...and only a real turn takes the turn
|
|
237
244
|
if (r.effort) out.effort = clean(String(r.effort));
|
|
238
245
|
}
|
|
239
246
|
const u = m.usage || {};
|
|
@@ -260,6 +267,19 @@ function readHarness(file, clean = (x) => x) {
|
|
|
260
267
|
if (!resolved.has(id)) out.openCall = { verb: info.verb, sinceTs: info.ts };
|
|
261
268
|
}
|
|
262
269
|
|
|
270
|
+
// ── Whose turn (R1). Derived here, from what the pass already collected — no second walk. ──
|
|
271
|
+
// The ORDER of these three branches is the honesty property the whole feature rests on (R4).
|
|
272
|
+
// `tool` is tested FIRST, so any unresolved call claims the turn. A crashed session's dangling
|
|
273
|
+
// tool_use is a PERMANENT phantom (2/25 real transcripts), and a phantom is by definition an
|
|
274
|
+
// unresolved call — so a phantom always lands on `tool`, and `user` (the only turn from which
|
|
275
|
+
// the strong `needs you` claim can be built) is UNREACHABLE for it. Not a guard that rejects
|
|
276
|
+
// the phantom: a value it cannot construct. Reordering these branches silently converts a
|
|
277
|
+
// corpse into "waiting on you", which is the one lie this surface must not tell.
|
|
278
|
+
if (out.openCall) out.turn = 'tool'; // awaiting a tool — OR the PM's approval of one
|
|
279
|
+
else if (lastRole === 'assistant') out.turn = 'user'; // the model stopped; the human is up
|
|
280
|
+
else if (lastRole === 'user') out.turn = 'agent'; // a prompt/result landed; the model is up
|
|
281
|
+
// else: null — nothing turn-taking was parseable, so no claim is made.
|
|
282
|
+
|
|
263
283
|
if (!out.model) out.missing.model = 'the transcript names no model yet';
|
|
264
284
|
if (!out.effort) out.missing.effort = 'the transcript records no effort level';
|
|
265
285
|
if (toolTotal) out.tools = { counts: toolCounts, total: toolTotal };
|
|
@@ -271,11 +291,13 @@ function readHarness(file, clean = (x) => x) {
|
|
|
271
291
|
else out.missing.tokens = 'the transcript records no token usage yet';
|
|
272
292
|
if (!out.skills) out.missing.skills = 'the CLI listed no skills for this session';
|
|
273
293
|
if (!out.turns) out.missing.turns = 'no model turns in the transcript yet';
|
|
274
|
-
// NOTE: `title` and `
|
|
275
|
-
// UI RENDERS in place of a value;
|
|
276
|
-
// (agent · cwd for an untitled session; the plain `working`/`quiet` word when no call
|
|
277
|
-
// Putting them here would break the map's contract —
|
|
278
|
-
// and put a reason on screen where a perfectly good
|
|
294
|
+
// NOTE: `title`, `openCall` and `turn` deliberately get NO `missing` entry. This map carries
|
|
295
|
+
// reasons the UI RENDERS in place of a value; all three instead fall back to a real shipped
|
|
296
|
+
// rendering (agent · cwd for an untitled session; the plain `working`/`quiet` word when no call
|
|
297
|
+
// is open and when the turn is unknown). Putting them here would break the map's contract —
|
|
298
|
+
// "empty missing == everything was readable" — and put a reason on screen where a perfectly good
|
|
299
|
+
// value already goes. For `turn` that fallback IS the feature's refusal rule (R5): no second
|
|
300
|
+
// source, no claim, and the row reads exactly as it did before this existed.
|
|
279
301
|
|
|
280
302
|
return out;
|
|
281
303
|
}
|