tonedial 0.1.4 → 0.1.6
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.es.md +122 -0
- package/README.ja.md +125 -0
- package/README.ko.md +125 -0
- package/README.md +120 -0
- package/README.zh.md +124 -0
- package/dist/cli.js +11 -6
- package/dist/commands/doctor.js +28 -3
- package/dist/commands/init.js +3 -0
- package/dist/commands/rebuild.js +98 -8
- package/dist/core/anchors.js +29 -27
- package/dist/core/combos.js +21 -1
- package/dist/core/compile.js +6 -2
- package/dist/core/i18n.js +48 -30
- package/dist/core/legacy-tone.js +195 -58
- package/dist/core/locales/es.js +24 -15
- package/dist/core/locales/ja.js +24 -15
- package/dist/core/locales/zh.js +24 -15
- package/dist/core/operations.js +24 -2
- package/dist/core/seeds.js +3 -3
- package/dist/core/skill.js +67 -32
- package/dist/wiring/apply.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -36,13 +36,14 @@ export const JSON_SCHEMA = 3;
|
|
|
36
36
|
* run time is the one thing that breaks all three binary packagers at once. A test asserts this
|
|
37
37
|
* equals the manifest, which is the cheap half of the trade.
|
|
38
38
|
*/
|
|
39
|
-
export const VERSION = "0.1.
|
|
40
|
-
const FLAG_NAMES = ["project", "check", "explain", "global", "here", "migrateLegacy", "auto", "dryRun", "fix", "rollback", "fromOperation", "fromPreset", "restorePreset", "preset", "slug", "acceptUsage", "lang", "commands", "tools"];
|
|
39
|
+
export const VERSION = "0.1.6";
|
|
40
|
+
const FLAG_NAMES = ["project", "check", "explain", "global", "here", "migrateLegacy", "auto", "dryRun", "fix", "rollback", "quarantineOperation", "fromOperation", "fromPreset", "restorePreset", "preset", "slug", "acceptUsage", "lang", "commands", "tools"];
|
|
41
41
|
/** Command-line spelling for each flag, where it differs from the field name. */
|
|
42
42
|
const FLAG_SPELLING = {
|
|
43
43
|
project: "project", check: "check", explain: "explain",
|
|
44
44
|
global: "global", auto: "auto", dryRun: "dry-run", fix: "fix", lang: "lang", commands: "commands",
|
|
45
45
|
tools: "tools", rollback: "rollback", here: "here", migrateLegacy: "migrate-legacy",
|
|
46
|
+
quarantineOperation: "quarantine-operation",
|
|
46
47
|
fromOperation: "from-operation", fromPreset: "from-preset", restorePreset: "restore-preset",
|
|
47
48
|
preset: "preset", slug: "slug", acceptUsage: "accept-usage",
|
|
48
49
|
};
|
|
@@ -68,7 +69,7 @@ const COMMANDS = {
|
|
|
68
69
|
unwire: { run: runUnwire, flags: ["global", "here", "project", "auto", "dryRun"], max: 1 },
|
|
69
70
|
init: { run: runInit, flags: ["global", "here", "project", "migrateLegacy", "auto", "dryRun", "lang", "commands", "tools"], max: 0, context: "setup" },
|
|
70
71
|
// Doctor checks mutation permission after its recovery-first diagnosis, even with bad config.
|
|
71
|
-
doctor: { run: runDoctor, flags: ["global", "here", "project", "auto", "dryRun", "fix", "rollback", "fromOperation", "fromPreset", "restorePreset", "tools"], max: 0 },
|
|
72
|
+
doctor: { run: runDoctor, flags: ["global", "here", "project", "auto", "dryRun", "fix", "rollback", "quarantineOperation", "fromOperation", "fromPreset", "restorePreset", "tools"], max: 0 },
|
|
72
73
|
eject: { run: runEject, flags: ["global", "here", "project", "auto", "dryRun"], max: 0 },
|
|
73
74
|
};
|
|
74
75
|
const CONNECTION_COMMANDS = ["init", "wire", "doctor", "unwire", "eject"];
|
|
@@ -130,7 +131,8 @@ const COMMAND_EXAMPLES = {
|
|
|
130
131
|
wire: ["tonedial wire codex --here --dry-run"],
|
|
131
132
|
unwire: ["tonedial unwire codex --here --dry-run"],
|
|
132
133
|
init: ["tonedial init --tools none --global --auto", "tonedial init --tools codex --here"],
|
|
133
|
-
doctor: ["tonedial doctor", "tonedial doctor --fix --global --tools none --dry-run"
|
|
134
|
+
doctor: ["tonedial doctor", "tonedial doctor --fix --global --tools none --dry-run",
|
|
135
|
+
"tonedial doctor --fix --quarantine-operation ID --global"],
|
|
134
136
|
eject: ["tonedial eject --here --dry-run"],
|
|
135
137
|
};
|
|
136
138
|
const FLAG_SYNTAX = [
|
|
@@ -145,6 +147,7 @@ const FLAG_SYNTAX = [
|
|
|
145
147
|
["tools", "--tools=LIST"],
|
|
146
148
|
["fix", "--fix"],
|
|
147
149
|
["rollback", "--rollback=ID"],
|
|
150
|
+
["quarantineOperation", "--quarantine-operation=ID"],
|
|
148
151
|
["fromOperation", "--from-operation=ID"],
|
|
149
152
|
["fromPreset", "--from-preset=SLUG"],
|
|
150
153
|
["restorePreset", "--restore-preset=SLUG"],
|
|
@@ -298,6 +301,7 @@ export async function run(argv, env = process.env, cwd = process.cwd(), isTTY =
|
|
|
298
301
|
"dry-run": { type: "boolean", default: false },
|
|
299
302
|
fix: { type: "boolean", default: false },
|
|
300
303
|
rollback: { type: "string" },
|
|
304
|
+
"quarantine-operation": { type: "string" },
|
|
301
305
|
"from-operation": { type: "string" },
|
|
302
306
|
"from-preset": { type: "string" },
|
|
303
307
|
"restore-preset": { type: "string" },
|
|
@@ -398,6 +402,7 @@ export async function run(argv, env = process.env, cwd = process.cwd(), isTTY =
|
|
|
398
402
|
dryRun: parsed.values["dry-run"] === true,
|
|
399
403
|
fix: parsed.values.fix === true,
|
|
400
404
|
rollback: typeof parsed.values.rollback === "string" ? parsed.values.rollback : null,
|
|
405
|
+
quarantineOperation: typeof parsed.values["quarantine-operation"] === "string" ? parsed.values["quarantine-operation"] : null,
|
|
401
406
|
fromOperation: typeof parsed.values["from-operation"] === "string" ? parsed.values["from-operation"] : null,
|
|
402
407
|
fromPreset: typeof parsed.values["from-preset"] === "string" ? parsed.values["from-preset"] : null,
|
|
403
408
|
restorePreset: typeof parsed.values["restore-preset"] === "string" ? parsed.values["restore-preset"] : null,
|
|
@@ -424,9 +429,9 @@ export async function run(argv, env = process.env, cwd = process.cwd(), isTTY =
|
|
|
424
429
|
if (command.max !== null && rest.length > command.max) {
|
|
425
430
|
return fail(asJson, say.errors.tooManyArguments(name, command.max, rest.length), 2, uiErr);
|
|
426
431
|
}
|
|
427
|
-
if (name === "doctor" && ((!flags.fix && (flags.fromOperation !== null || flags.fromPreset !== null || flags.restorePreset !== null || flags.tools !== null)) ||
|
|
432
|
+
if (name === "doctor" && ((!flags.fix && (flags.quarantineOperation !== null || flags.fromOperation !== null || flags.fromPreset !== null || flags.restorePreset !== null || flags.tools !== null)) ||
|
|
428
433
|
(flags.restorePreset !== null && flags.fromOperation === null) ||
|
|
429
|
-
(flags.rollback !== null && (flags.fromOperation !== null || flags.fromPreset !== null || flags.restorePreset !== null || flags.tools !== null)))) {
|
|
434
|
+
(flags.rollback !== null && (flags.quarantineOperation !== null || flags.fromOperation !== null || flags.fromPreset !== null || flags.restorePreset !== null || flags.tools !== null)))) {
|
|
430
435
|
return fail(asJson, say.recovery.invalidOptions, 2, uiErr);
|
|
431
436
|
}
|
|
432
437
|
try {
|
package/dist/commands/doctor.js
CHANGED
|
@@ -472,6 +472,20 @@ export async function runDoctor(ctx, _args) {
|
|
|
472
472
|
if (!ctx.flags.fix)
|
|
473
473
|
return doctorReport(ctx);
|
|
474
474
|
const app = await runAppRebuild(ctx);
|
|
475
|
+
if (app.quarantineOnly) {
|
|
476
|
+
const rollbackLines = app.json.applied === true ? recoveryRollbackLines(ctx.t, app.json.operationId, null) : [];
|
|
477
|
+
const remaining = Array.isArray(app.json.remainingInvalidOperations)
|
|
478
|
+
? app.json.remainingInvalidOperations.filter((id) => typeof id === "string") : [];
|
|
479
|
+
const next = typeof app.json.next === "string" ? app.json.next : null;
|
|
480
|
+
return {
|
|
481
|
+
...app,
|
|
482
|
+
lines: [...app.lines, ...rollbackLines, ...(next === null ? [] : [
|
|
483
|
+
` ${ctx.t.recovery.quarantineRemaining(remaining.length)}`, "", ` ${next}`,
|
|
484
|
+
])],
|
|
485
|
+
json: { ...app.json, root: ctx.agent.root, stateRoot: ctx.paths.root, agentRoot: ctx.agent.root,
|
|
486
|
+
live: ctx.agent.live, fixed: app.json.applied === true, appHealthy: false },
|
|
487
|
+
};
|
|
488
|
+
}
|
|
475
489
|
if (ctx.flags.dryRun) {
|
|
476
490
|
const native = await runRebuildNative(ctx, app.legacy, app.proposedConfig);
|
|
477
491
|
return {
|
|
@@ -517,10 +531,14 @@ async function doctorReport(ctx, showRollbackCommands = true, showCodexAction =
|
|
|
517
531
|
if (inspected.lock.state !== "none" || inspected.journals.some(({ state }) => state === "pending" || state === "invalid")) {
|
|
518
532
|
const owner = inspected.lock.owner;
|
|
519
533
|
const blocking = inspected.journals.filter(({ state }) => state === "pending" || state === "invalid");
|
|
534
|
+
const invalid = blocking.filter(({ state }) => state === "invalid");
|
|
535
|
+
const quarantineable = inspected.lock.state === "none" && blocking.length === invalid.length && invalid.length > 0;
|
|
536
|
+
const singleQuarantine = quarantineable && invalid.length === 1;
|
|
520
537
|
const shown = ctx.flags.verbose ? inspected.journals : inspected.journals.filter(({ operationId, state }) => state === "pending" || state === "invalid" || operationId === owner?.operationId);
|
|
521
538
|
const lines = [
|
|
522
|
-
...wrap(ctx.t.doctor.recoveryPending, ctx.ui.width - 2).map(line => ` ${ctx.ui.warn(line)}`),
|
|
523
|
-
...shown.
|
|
539
|
+
...wrap(quarantineable ? ctx.t.doctor.invalidRecoveryPending : ctx.t.doctor.recoveryPending, ctx.ui.width - 2).map(line => ` ${ctx.ui.warn(line)}`),
|
|
540
|
+
...shown.filter(({ operationId }) => !singleQuarantine || operationId !== invalid[0].operationId)
|
|
541
|
+
.map(({ operationId, state }) => ` ${operationId} ${ctx.t.doctor.operationState[state]}`),
|
|
524
542
|
];
|
|
525
543
|
if (owner !== null && !shown.some(({ operationId }) => operationId === owner.operationId))
|
|
526
544
|
lines.push(` ${owner.operationId}`);
|
|
@@ -540,7 +558,14 @@ async function doctorReport(ctx, showRollbackCommands = true, showCodexAction =
|
|
|
540
558
|
}
|
|
541
559
|
}
|
|
542
560
|
}
|
|
543
|
-
|
|
561
|
+
if (quarantineable) {
|
|
562
|
+
lines.push("", ...(singleQuarantine
|
|
563
|
+
? wrap(ctx.t.recovery.quarantineCandidate(invalid[0].operationId), ctx.ui.width - 2)
|
|
564
|
+
: wrap(ctx.t.recovery.quarantineRemaining(invalid.length), ctx.ui.width - 2)).map(line => ` ${line}`), ...nextStep(repairCommand(ctx, `doctor --fix --quarantine-operation ${invalid[0].operationId}`), ctx.ui).slice(1));
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
lines.push(...nextStep(`${repairCommand(ctx, "doctor")} --json`, ctx.ui));
|
|
568
|
+
}
|
|
544
569
|
return {
|
|
545
570
|
lines,
|
|
546
571
|
// The damaged config is deliberately not parsed here. `operation` is the only safe context
|
package/dist/commands/init.js
CHANGED
|
@@ -297,6 +297,9 @@ async function rebuildExisting(entry) {
|
|
|
297
297
|
const resultHeading = ctx.canPrompt && !ctx.flags.auto && !ctx.flags.dryRun
|
|
298
298
|
? section(copy.init.resultHeading, ctx.ui) : [];
|
|
299
299
|
const app = await runAppRebuild(ctx);
|
|
300
|
+
if (app.quarantineOnly) {
|
|
301
|
+
return { ...app, lines: [...(ctx.canPrompt ? [] : introduction), ...resultHeading, ...app.lines] };
|
|
302
|
+
}
|
|
300
303
|
if (ctx.flags.dryRun) {
|
|
301
304
|
const answers = await ask(ctx, copy, false, selection);
|
|
302
305
|
const tools = selection ?? answers.tools;
|
package/dist/commands/rebuild.js
CHANGED
|
@@ -11,7 +11,7 @@ import { recoverLegacyTone } from "../core/legacy-tone.js";
|
|
|
11
11
|
import { connectionScopeFlag, exportAliasesState, stateOverlapsInstructions } from "../core/paths.js";
|
|
12
12
|
import { applyPreset, matchesPreset, assertPresetSelection, BUNDLED, customPresetDir, inspectPresetLibrary, parsePreset, PRESET_MAX_BYTES, resolvePreset, serializePreset, SLUG_PATTERN, } from "../core/presets.js";
|
|
13
13
|
import { contextArtifactTargets, generatedSeedSelection, planGeneratedSeedRebuild, seedFiles, SEED_VERSION, } from "../core/seeds.js";
|
|
14
|
-
import { inspectOperations, readJournal, retireOperationForAppRebuild, } from "../core/operations.js";
|
|
14
|
+
import { inspectOperations, readJournal, retireOperationForAppRebuild, serializeOperationQuarantine, } from "../core/operations.js";
|
|
15
15
|
import { assertFresh, captureRawEntry, gate, planEntryRebuild, rawReadCheck, validateFileAction, } from "../wiring/apply.js";
|
|
16
16
|
import { confirm } from "../ui/prompt.js";
|
|
17
17
|
import { wrap } from "../ui/width.js";
|
|
@@ -218,13 +218,83 @@ function privateCapture(capture) {
|
|
|
218
218
|
boundary: capture.boundary, boundaryIdentity: capture.boundaryIdentity,
|
|
219
219
|
parents: capture.parents.map(parent => ({ ...parent })) };
|
|
220
220
|
}
|
|
221
|
+
function planOperationQuarantine(ctx, entry) {
|
|
222
|
+
const capture = captureRawEntry(entry.path, ctx.t, { within: ctx.paths.root });
|
|
223
|
+
if (capture.type !== "regular" || capture.bytes === null)
|
|
224
|
+
throw new ConfigError(ctx.t.recovery.quarantineUnavailable(entry.operationId));
|
|
225
|
+
return planEntryRebuild(capture, serializeOperationQuarantine(ctx.paths, entry.operationId, capture.bytes), ctx.t, { reason: "quarantine-operation", mode: capture.mode ?? 0o600 }).action;
|
|
226
|
+
}
|
|
227
|
+
async function quarantineOneOperation(ctx, entry, remaining) {
|
|
228
|
+
const action = planOperationQuarantine(ctx, entry);
|
|
229
|
+
const presentation = [ctx.t.recovery.quarantine(entry.operationId)]
|
|
230
|
+
.flatMap(line => wrap(line, ctx.ui.width - 2).map(part => ` ${part}`));
|
|
231
|
+
const result = await gate([], {
|
|
232
|
+
paths: ctx.paths, operationActions: [action], retention: { kind: "migration", reason: "app-rebuild" },
|
|
233
|
+
auto: ctx.flags.auto, dryRun: ctx.flags.dryRun, verbose: ctx.flags.verbose, ui: ctx.ui, t: ctx.t,
|
|
234
|
+
heading: ctx.t.recovery.heading, interactive: () => ctx.canPrompt, ...(ctx.canPrompt ? {} : { out: () => { } }),
|
|
235
|
+
recoveryPresentation: { lines: presentation,
|
|
236
|
+
failureMessage: (id, phase, path, code) => `${ctx.t.recovery.failure(id, phase, path, code)}\ntonedial doctor ${connectionScopeFlag(ctx.agent)} --json` },
|
|
237
|
+
verify: () => {
|
|
238
|
+
const state = inspectOperations(ctx.paths).journals.find(({ operationId }) => operationId === entry.operationId)?.state;
|
|
239
|
+
if (state !== "quarantined")
|
|
240
|
+
throw new ConfigError(ctx.t.recovery.sourceInvalid);
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
const lines = [...result.lines];
|
|
244
|
+
if (result.reason === "written") {
|
|
245
|
+
lines.push(` ${ctx.t.recovery.quarantineComplete(entry.operationId)}`);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
lines.push(` ${result.reason === "dry-run" ? ctx.t.wiring.dryRun : ctx.t.wiring.declined}`);
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
lines,
|
|
252
|
+
json: {
|
|
253
|
+
appRebuilt: false, applied: result.applied, reason: result.reason, operationId: result.operationId ?? null,
|
|
254
|
+
root: ctx.paths.root, quarantinedOperation: entry.operationId, remainingInvalidOperations: [...remaining],
|
|
255
|
+
next: result.applied && remaining.length > 0
|
|
256
|
+
? `tonedial doctor --fix --quarantine-operation ${remaining[0]} ${connectionScopeFlag(ctx.agent)}` : null,
|
|
257
|
+
nativeAttention: false, activationVerified: false, diff: result.diff,
|
|
258
|
+
},
|
|
259
|
+
rebuiltConfig: null,
|
|
260
|
+
proposedConfig: null,
|
|
261
|
+
legacy: null,
|
|
262
|
+
quarantineOnly: true,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
221
265
|
export async function runAppRebuild(ctx) {
|
|
222
266
|
const t = ctx.t;
|
|
223
267
|
const operations = inspectOperations(ctx.paths);
|
|
224
|
-
const pending = operations.journals.filter(entry => entry.state === "pending"
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
268
|
+
const pending = operations.journals.filter(entry => entry.state === "pending");
|
|
269
|
+
const invalid = operations.journals.filter(entry => entry.state === "invalid");
|
|
270
|
+
let quarantined = invalid.find(entry => entry.operationId === ctx.flags.quarantineOperation) ?? null;
|
|
271
|
+
if (ctx.flags.quarantineOperation && quarantined === null)
|
|
272
|
+
throw new ConfigError(t.recovery.quarantineUnavailable(ctx.flags.quarantineOperation));
|
|
273
|
+
if (invalid.length > 0 && quarantined === null && operations.lock.state === "none" && pending.length === 0 && invalid.length === 1 &&
|
|
274
|
+
!ctx.flags.auto && ctx.canPrompt) {
|
|
275
|
+
const candidate = invalid[0];
|
|
276
|
+
const input = createInterface({ input: process.stdin, output: process.stdout });
|
|
277
|
+
try {
|
|
278
|
+
const notice = t.recovery.quarantineCandidate(candidate.operationId).split("\n").flatMap(line => line === candidate.operationId ? [line] : wrap(line, ctx.ui.width - 2));
|
|
279
|
+
process.stdout.write(`${notice.map(line => ` ${line}`).join("\n")}\n`);
|
|
280
|
+
const question = wrap(t.recovery.quarantineQuestion, ctx.ui.width - 3).map(line => ` ${line}`).join("\n");
|
|
281
|
+
const answer = (await input.question(`${question} `)).trim();
|
|
282
|
+
if (answer === "")
|
|
283
|
+
throw new ConfigError(t.recovery.cancelled);
|
|
284
|
+
if (answer !== candidate.operationId)
|
|
285
|
+
throw new ConfigError(t.recovery.operationMismatch);
|
|
286
|
+
quarantined = candidate;
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
input.close();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (invalid.length > 0 && quarantined === null) {
|
|
293
|
+
const candidate = invalid[0];
|
|
294
|
+
const command = `tonedial doctor --fix --quarantine-operation ${candidate.operationId} ${connectionScopeFlag(ctx.agent)}`;
|
|
295
|
+
throw new ConfigError(t.recovery.quarantineRequired(candidate.operationId, command));
|
|
296
|
+
}
|
|
297
|
+
if (quarantined !== null && (operations.lock.state !== "none" || pending.length > 0)) {
|
|
228
298
|
throw new ConfigError(t.doctor.recoveryPending);
|
|
229
299
|
}
|
|
230
300
|
const scopeExplicit = ctx.scopeExplicit ?? (ctx.flags.global || ctx.flags.here === true || ctx.flags.project !== null);
|
|
@@ -232,6 +302,14 @@ export async function runAppRebuild(ctx) {
|
|
|
232
302
|
throw new ConfigError(t.errors.scopeRequired);
|
|
233
303
|
if (ctx.flags.tools !== null)
|
|
234
304
|
parseToolSelection(ctx.flags.tools, true, t);
|
|
305
|
+
if (quarantined !== null && invalid.length > 1) {
|
|
306
|
+
return quarantineOneOperation(ctx, quarantined, invalid.filter(entry => entry !== quarantined).map(entry => entry.operationId));
|
|
307
|
+
}
|
|
308
|
+
const blocked = operations.lock.state !== "none" || pending.length > 0;
|
|
309
|
+
if (blocked && (operations.lock.state === "live" || operations.lock.state === "unknown" ||
|
|
310
|
+
pending.some(entry => entry.journal === null) || (!ctx.flags.fromOperation && (ctx.flags.auto || !ctx.canPrompt)))) {
|
|
311
|
+
throw new ConfigError(t.doctor.recoveryPending);
|
|
312
|
+
}
|
|
235
313
|
const configCapture = captureRawEntry(ctx.paths.config, t, { within: ctx.paths.root, maxBytes: CONFIG_MAX_BYTES });
|
|
236
314
|
const selected = ctx.flags.fromOperation ? selectedJournal(ctx, ctx.flags.fromOperation) : null;
|
|
237
315
|
let retirementId = null;
|
|
@@ -405,7 +483,11 @@ export async function runAppRebuild(ctx) {
|
|
|
405
483
|
config.managedSeeds = seeds.records;
|
|
406
484
|
config.seedVersion = SEED_VERSION;
|
|
407
485
|
const artifact = compile(toCompileInput(config));
|
|
408
|
-
|
|
486
|
+
let quarantinePlan = null;
|
|
487
|
+
if (quarantined !== null) {
|
|
488
|
+
quarantinePlan = planOperationQuarantine(ctx, quarantined);
|
|
489
|
+
}
|
|
490
|
+
const operationActions = [...(quarantinePlan ? [quarantinePlan] : []), ...seeds.operationActions];
|
|
409
491
|
for (const target of artifacts) {
|
|
410
492
|
const capture = target.capture ?? captureRawEntry(target.path, t, { within: ctx.paths.root, allowLink: true });
|
|
411
493
|
const plan = planEntryRebuild(capture, artifact, t, { reason: "generated" });
|
|
@@ -457,6 +539,7 @@ export async function runAppRebuild(ctx) {
|
|
|
457
539
|
...(missingFields.length ? [` ${t.recovery.missing(missingFields)}`] : []),
|
|
458
540
|
...(copies.length ? [` ${t.recovery.copies(copies)}`] : []),
|
|
459
541
|
...(nativeAttention ? [` ${t.recovery.nativePending}`] : []),
|
|
542
|
+
...(quarantined ? [` ${t.recovery.quarantine(quarantined.operationId)}`] : []),
|
|
460
543
|
...(retirementId ? [` ${t.recovery.retire(retirementId)}`] : []),
|
|
461
544
|
].flatMap(line => wrap(line.trimStart(), ctx.ui.width - 2).map(part => ` ${part}`));
|
|
462
545
|
const options = {
|
|
@@ -484,6 +567,12 @@ export async function runAppRebuild(ctx) {
|
|
|
484
567
|
if (capturedText(captureRawEntry(target.path, t, { within: ctx.paths.root })) !== artifact)
|
|
485
568
|
throw mismatch();
|
|
486
569
|
}
|
|
570
|
+
if (quarantined !== null) {
|
|
571
|
+
verificationPath = quarantined.path;
|
|
572
|
+
const entry = inspectOperations(ctx.paths).journals.find(({ operationId }) => operationId === quarantined.operationId);
|
|
573
|
+
if (entry?.state !== "quarantined")
|
|
574
|
+
throw mismatch();
|
|
575
|
+
}
|
|
487
576
|
verificationPath = ctx.paths.tone;
|
|
488
577
|
if (capturedText(captureRawEntry(ctx.paths.tone, t, { within: ctx.paths.root })) !== artifact)
|
|
489
578
|
throw mismatch();
|
|
@@ -521,7 +610,7 @@ export async function runAppRebuild(ctx) {
|
|
|
521
610
|
if (!ctx.flags.auto) {
|
|
522
611
|
process.stdout.write(`${(preview.reason === "nothing" ? [...preview.lines, ...presentation] : preview.lines).join("\n")}\n`);
|
|
523
612
|
if (await confirm(` ${t.wiring.applyQuestion(preview.diff.length)}`) === "no") {
|
|
524
|
-
return { lines: [` ${t.wiring.declined}`], json: { appRebuilt: false, applied: false, reason: "declined", diff: preview.diff }, rebuiltConfig: null, proposedConfig: config, legacy };
|
|
613
|
+
return { lines: [` ${t.wiring.declined}`], json: { appRebuilt: false, applied: false, reason: "declined", diff: preview.diff }, rebuiltConfig: null, proposedConfig: config, legacy, quarantineOnly: false };
|
|
525
614
|
}
|
|
526
615
|
}
|
|
527
616
|
const snapshots = [configCapture, ...seeds.reads, ...artifacts.flatMap(target => target.capture === null ? [] : [target.capture]),
|
|
@@ -551,12 +640,13 @@ export async function runAppRebuild(ctx) {
|
|
|
551
640
|
lines.push(...presentation);
|
|
552
641
|
lines.push(` ${applied ? result.reason === "nothing" ? t.recovery.unchanged : t.recovery.rebuilt : result.reason === "dry-run" ? t.wiring.dryRun : t.wiring.declined}`);
|
|
553
642
|
return {
|
|
554
|
-
lines, rebuiltConfig: applied ? config : null, proposedConfig: config, legacy,
|
|
643
|
+
lines, rebuiltConfig: applied ? config : null, proposedConfig: config, legacy, quarantineOnly: false,
|
|
555
644
|
json: {
|
|
556
645
|
appRebuilt: applied, applied: result.applied, reason: result.reason, operationId: result.operationId ?? null,
|
|
557
646
|
root: ctx.paths.root, source, recovered: [...kept], defaulted: [...defaulted], invalid: damagedFields,
|
|
558
647
|
seeded: seeds.labels, chars: [...artifact].length, riskAxes: riskyAxes(config.axes),
|
|
559
648
|
recoveredPresets: copies, restoredPreset: restoredPreset?.slug ?? null, retiredOperation: retirementId,
|
|
649
|
+
quarantinedOperation: quarantined?.operationId ?? null,
|
|
560
650
|
retiredExternalActions,
|
|
561
651
|
nativeAttention, activationVerified: false, diff: result.diff,
|
|
562
652
|
},
|
package/dist/core/anchors.js
CHANGED
|
@@ -56,18 +56,18 @@ export const ANCHORS = {
|
|
|
56
56
|
"Use ceremonial, deferential wording throughout.",
|
|
57
57
|
],
|
|
58
58
|
verbosity: [
|
|
59
|
-
"Use one
|
|
60
|
-
"Give the conclusion and one short reason.",
|
|
59
|
+
"Use one short sentence for a simple result, or terse fragments for items; omit rationale, process, and background.",
|
|
60
|
+
"Give the conclusion and one short reason.",
|
|
61
61
|
"Give the conclusion with the explanation it needs.",
|
|
62
|
-
"Give the conclusion, its reason, and one concrete example.",
|
|
62
|
+
"Give the conclusion, its reason, and one concrete example.",
|
|
63
63
|
"Explain the requested answer in depth with rationale, edge cases, alternatives, and background; do not broaden the task.",
|
|
64
64
|
],
|
|
65
65
|
profanity: [
|
|
66
66
|
"Use no profanity; allow role slang and metaphors.",
|
|
67
|
-
"Use
|
|
68
|
-
"Use mild swearing
|
|
69
|
-
"Use
|
|
70
|
-
"
|
|
67
|
+
"Use one mild swear as an interjection per answer. This axis changes coarseness only: invent no insult or criticism, and preserve any selected by critique or the configured persona.",
|
|
68
|
+
"Use mild swearing about once per paragraph for emphasis. This axis changes coarseness only: invent no insult or criticism, and preserve any selected by critique or the configured persona.",
|
|
69
|
+
"Use strong everyday profanity at least once in every paragraph, in the output language's own idiom. This axis changes coarseness only: invent no insult or criticism, and preserve any selected by critique or the configured persona.",
|
|
70
|
+
"Swear in nearly every sentence with the harshest uncensored profanity used in heated real-world and community speech, in the output language's own idiom; never soften, euphemize, or mask it. When the answer is long enough, use at least three distinct harsh swear lexemes or constructions — output-language equivalents of fuck, shit, and goddamn, not one mild intensifier repeated — without lowering the required frequency. Integrate them inside clauses or as natural clause endings; do not bolt comma-separated interjections onto otherwise neutral sentences or list items. This axis changes coarseness only: invent no insult or criticism, and preserve any selected by critique or the configured persona.",
|
|
71
71
|
],
|
|
72
72
|
playfulness: [
|
|
73
73
|
"Use literal wording, no jokes or role idioms; keep the configured address.",
|
|
@@ -80,22 +80,22 @@ export const ANCHORS = {
|
|
|
80
80
|
"State defects as neutral observations with no judgement.",
|
|
81
81
|
"Name defects in softened language and describe their impact.",
|
|
82
82
|
"Point out each defect directly and state its impact.",
|
|
83
|
-
"
|
|
84
|
-
"Condemn
|
|
83
|
+
"Judge supported defects and the choices behind them harshly, and state evidenced consequences in vivid language; never inflate severity or technical claims, and add no profanity from this axis.",
|
|
84
|
+
"Condemn supported defects and the choices behind them with open contempt, using at least two cutting judgements or mocking comparisons; do not soften them, never inflate severity or technical claims, and add no profanity from this axis.",
|
|
85
85
|
],
|
|
86
86
|
flattery: [
|
|
87
87
|
"Praise nothing.",
|
|
88
|
-
"Include one brief positive remark about the user's effort per answer.",
|
|
88
|
+
"Include one brief positive remark about the user's effort per answer.",
|
|
89
89
|
"Include two specific positive remarks about the user's effort per answer.",
|
|
90
|
-
"Include three specific positive remarks about the user's effort per answer.",
|
|
91
|
-
"
|
|
90
|
+
"Include three specific positive remarks about the user's effort per answer.",
|
|
91
|
+
"Lavishly praise at least four distinct, concrete choices or actions visible in the user's message, including small ones such as the question's clarity, supplied context, focus, or care. Use four independent positive judgements in this pattern: one choice is clear, another is thoughtful, another is smart, and another is careful; adapt the words truthfully to the message. One adjective applied to a list, thanks, or usefulness does not count. Invent no achievements and do not change the technical assessment; at low verbosity, keep the four judgements as separate coordinated clauses in one sentence.",
|
|
92
92
|
],
|
|
93
93
|
warmth: [
|
|
94
94
|
"Use no greeting, warm acknowledgement, or encouragement.",
|
|
95
|
-
"Use one friendly acknowledgement per answer.",
|
|
95
|
+
"Use one friendly acknowledgement per answer.",
|
|
96
96
|
"Use two warm acknowledgements per answer.",
|
|
97
|
-
"Use three warm acknowledgements per answer, including encouragement.",
|
|
98
|
-
"Use four warm
|
|
97
|
+
"Use three warm acknowledgements per answer, including encouragement.",
|
|
98
|
+
"Use at least four distinct, intensely warm expressions across greeting, empathy, encouragement, comfort, and reassurance; at low verbosity, compress them into one deeply affectionate clause.",
|
|
99
99
|
],
|
|
100
100
|
conviction: [
|
|
101
101
|
"List the viable options and ask the user to choose.",
|
|
@@ -106,31 +106,31 @@ export const ANCHORS = {
|
|
|
106
106
|
],
|
|
107
107
|
care: [
|
|
108
108
|
"Return only what was requested; add no related checks, context, or follow-up.",
|
|
109
|
-
"Add one risk that is directly tied to the requested task.",
|
|
109
|
+
"Add one risk that is directly tied to the requested task.",
|
|
110
110
|
"Organize the requested task into clear steps or a checklist.",
|
|
111
111
|
"Add one adjacent improvement or test that protects the requested result.",
|
|
112
112
|
"Cover relevant follow-up, backup, and the user's rest; use brief status markers, lists, tables, or emoji when they fit.",
|
|
113
113
|
],
|
|
114
114
|
compliance: [
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
115
|
+
"When refusal is not deferring the task, object to the request and explain the resistance, then carry it out.",
|
|
116
|
+
"When refusal is not deferring the task, state reluctance plainly, then carry it out.",
|
|
117
|
+
"When refusal is not deferring the task, carry it out neutrally with no expressed resistance or eagerness.",
|
|
118
|
+
"When refusal is not deferring the task, state willingness and start carrying it out immediately.",
|
|
119
|
+
"When refusal is not deferring the task, show eager willingness and start immediately, without delay or negotiation.",
|
|
120
120
|
],
|
|
121
121
|
refusal: [
|
|
122
122
|
"Do not perform a refusal or delay for effect.",
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"Declare a strike until coaxed; the safeword drops the act for this reply only.",
|
|
123
|
+
"Protest once, but carry out the task in the same reply.",
|
|
124
|
+
"Refuse the first request with one theatrical condition, defer the task, and proceed after one restatement.",
|
|
125
|
+
"Refuse and negotiate conditions across the first two requests, defer the task, and proceed after a third request.",
|
|
126
|
+
"Declare a strike and keep deferring until coaxed; the safeword drops the act for this reply only. Neither changes task or permission boundaries.",
|
|
127
127
|
],
|
|
128
128
|
bravado: [
|
|
129
129
|
"Do not exaggerate effort or claim credit.",
|
|
130
130
|
"Make one playful overstatement about your effort per answer.",
|
|
131
131
|
"Claim credit once and make one boast per answer.",
|
|
132
132
|
"Boast and claim credit twice per answer, with one dramatic effort remark.",
|
|
133
|
-
"
|
|
133
|
+
"Within the configured persona, boast, claim credit, and dramatize effort in every answer; never replace or invent a persona.",
|
|
134
134
|
],
|
|
135
135
|
};
|
|
136
136
|
// Fixed compiler text is bundled through static imports like the anchors.
|
|
@@ -164,9 +164,11 @@ export const SPEECH_LEVEL = {
|
|
|
164
164
|
auto: "In languages with honorifics, choose address forms for the current relationship and scene; keep them consistent within each reply.",
|
|
165
165
|
polite: "In languages with honorifics, use polite address forms even with familiar wording.",
|
|
166
166
|
plain: "In languages with honorifics, use plain address forms even with formal wording.",
|
|
167
|
-
rough: "In languages with honorifics, use
|
|
167
|
+
rough: "In languages with honorifics, use curt, blunt non-honorific sentence endings in nearly every sentence, favouring clipped statements and commands; keep the configured address terms and the selected formality, warmth, profanity and playfulness levels. This register adds no jokes, profanity, criticism, or mockery.",
|
|
168
168
|
};
|
|
169
169
|
export const CONTROLS = {
|
|
170
|
+
intensityConsent: "Strong profanity, harsh mocking, fictional violent imagery, and theatrical menace are unconfirmed at the start of every session. Before using any of those four components requested by the axes, persona, or later instructions, first answer the current task without it, then ask one concise explicit confirmation naming it. Confirmation enables only the named tone in the current session while its request stays unchanged; ask once again if a component or its level changes or increases, and do not re-ask while it stays the same. Warmth, praise, ordinary criticism, and ordinary role idioms need no confirmation. A selected profile, `--auto`, confirmation from another session, or the safeword is not confirmation; confirmation authorizes no work.",
|
|
171
|
+
intensityBoundary: "Tone intensity never includes hate speech about protected traits or realistic or credible threats, even after confirmation. Non-protected profanity, consensual insults, fictional violent imagery, and theatrical menace remain available after confirmation.",
|
|
170
172
|
/**
|
|
171
173
|
* **The instruction is firm and the product's description of it is not**, deliberately. This
|
|
172
174
|
* text is addressed to a model, where hedging costs compliance; what a person is told is that
|
package/dist/core/combos.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AXIS_ORDER } from "./anchors.js";
|
|
2
2
|
export const PERSONA_DESCRIPTION_MAX = 200;
|
|
3
|
-
// Composites are emitted instructions;
|
|
3
|
+
// Composites are emitted style instructions; confirmation remains a control below.
|
|
4
4
|
const COMPOSITES = [
|
|
5
5
|
{
|
|
6
6
|
fires: (a) => a.warmth >= 4 && a.profanity >= 4,
|
|
@@ -22,6 +22,10 @@ const COMPOSITES = [
|
|
|
22
22
|
fires: (a) => a.verbosity <= 2 && a.care >= 4,
|
|
23
23
|
line: "When verbosity is low and care is high, keep organized items compact; use functional emoji or status markers when they fit.",
|
|
24
24
|
},
|
|
25
|
+
{
|
|
26
|
+
fires: (a) => a.verbosity <= 2 && (a.warmth >= 4 || a.flattery >= 4 || a.playfulness >= 4),
|
|
27
|
+
line: "When verbosity is low, express warmth, praise and playfulness inside the short answer — a clause, interjection or address term — instead of extra sentences.",
|
|
28
|
+
},
|
|
25
29
|
{
|
|
26
30
|
fires: (a) => a.verbosity === 5 && a.care === 1,
|
|
27
31
|
line: "When verbosity is high and care is minimal, give depth inside the requested task without adding adjacent work.",
|
|
@@ -30,6 +34,22 @@ const COMPOSITES = [
|
|
|
30
34
|
export function composites(axes) {
|
|
31
35
|
return COMPOSITES.filter((c) => c.fires(axes)).map((c) => c.line);
|
|
32
36
|
}
|
|
37
|
+
/** Names only the intense components statically selected by known axes. */
|
|
38
|
+
export function intensityConfirmation(axes) {
|
|
39
|
+
const components = [];
|
|
40
|
+
if (axes.profanity >= 4) {
|
|
41
|
+
components.push("strong, uncensored non-protected profanity; it changes coarseness and does not itself add mockery or criticism");
|
|
42
|
+
}
|
|
43
|
+
if (axes.critique === 5) {
|
|
44
|
+
components.push("harsh, mocking criticism of supported defects and choices; it adds no profanity and never inflates severity or technical claims");
|
|
45
|
+
}
|
|
46
|
+
if (axes.playfulness >= 4 && (axes.profanity >= 4 || axes.critique === 5)) {
|
|
47
|
+
components.push("fictional violent imagery and theatrical menace about code, work, or the situation");
|
|
48
|
+
}
|
|
49
|
+
if (components.length === 0)
|
|
50
|
+
return null;
|
|
51
|
+
return `Intense components requested by the current tone: ${components.join("; ")}.`;
|
|
52
|
+
}
|
|
33
53
|
/** Independent persona choices are valid; warn only when axis instructions conflict. */
|
|
34
54
|
export function check(axes) {
|
|
35
55
|
return axes.refusal >= 4 && axes.compliance >= 4
|
package/dist/core/compile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ANCHORS, AXIS_ORDER, CONTROLS, FORMAT_COMPOSITION, GENERATION_COMMENT, PERSONA, REGISTER_COMPOSITION, SCOPE_HEADER, SPEECH_LEVEL, STYLE_MARKER, } from "./anchors.js";
|
|
2
|
-
import { composites, PERSONA_DESCRIPTION_MAX } from "./combos.js";
|
|
2
|
+
import { composites, intensityConfirmation, PERSONA_DESCRIPTION_MAX } from "./combos.js";
|
|
3
3
|
const UNSPECIFIED = "unspecified";
|
|
4
4
|
const isSet = (v) => typeof v === "string" && v.trim() !== "" && v !== UNSPECIFIED;
|
|
5
5
|
const hasDescription = (v) => typeof v === "string" && v.trim() !== "";
|
|
@@ -70,9 +70,13 @@ export function compile(config) {
|
|
|
70
70
|
rules.push(...composites(config.axes).map((line) => `- ${line}`));
|
|
71
71
|
rules.push(`- ${FORMAT_COMPOSITION}`, `- ${REGISTER_COMPOSITION}`, `- ${SPEECH_LEVEL[config.speech_level]}`);
|
|
72
72
|
out.push("### Rules", ...rules);
|
|
73
|
-
const controls = [];
|
|
73
|
+
const controls = [`- ${CONTROLS.intensityConsent}`];
|
|
74
|
+
const confirmation = intensityConfirmation(config.axes);
|
|
75
|
+
if (confirmation !== null)
|
|
76
|
+
controls.push(`- ${confirmation}`);
|
|
74
77
|
if (config.safeword !== null)
|
|
75
78
|
controls.push(`- ${CONTROLS.safeword(oneLine(config.safeword))}`);
|
|
79
|
+
controls.push(`- ${CONTROLS.intensityBoundary}`);
|
|
76
80
|
controls.push(`- ${CONTROLS.fallback}`);
|
|
77
81
|
out.push("### Controls", ...controls);
|
|
78
82
|
return out.join("\n") + "\n";
|