@glbgpt/cli 0.1.0-test.4 → 0.1.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 +79 -6
- package/dist/src/auth/authClient.d.ts +21 -2
- package/dist/src/auth/authClient.js +67 -3
- package/dist/src/auth/authClient.js.map +1 -1
- package/dist/src/auth/loginFlow.js +5 -2
- package/dist/src/auth/loginFlow.js.map +1 -1
- package/dist/src/auth/loopbackServer.d.ts +2 -1
- package/dist/src/auth/loopbackServer.js +6 -3
- package/dist/src/auth/loopbackServer.js.map +1 -1
- package/dist/src/commands/accountCommand.js +10 -4
- package/dist/src/commands/accountCommand.js.map +1 -1
- package/dist/src/commands/configCommand.d.ts +5 -0
- package/dist/src/commands/configCommand.js +15 -2
- package/dist/src/commands/configCommand.js.map +1 -1
- package/dist/src/commands/execCommand.d.ts +6 -0
- package/dist/src/commands/execCommand.js +61 -9
- package/dist/src/commands/execCommand.js.map +1 -1
- package/dist/src/commands/loginCommand.js +9 -6
- package/dist/src/commands/loginCommand.js.map +1 -1
- package/dist/src/commands/mediaCommand.js +3 -2
- package/dist/src/commands/mediaCommand.js.map +1 -1
- package/dist/src/commands/skillCommand.d.ts +30 -0
- package/dist/src/commands/skillCommand.js +160 -0
- package/dist/src/commands/skillCommand.js.map +1 -0
- package/dist/src/config/configFile.d.ts +12 -0
- package/dist/src/config/configFile.js +12 -0
- package/dist/src/config/configFile.js.map +1 -1
- package/dist/src/config/env.d.ts +18 -0
- package/dist/src/config/env.js +57 -7
- package/dist/src/config/env.js.map +1 -1
- package/dist/src/core/catalog.d.ts +8 -1
- package/dist/src/core/catalog.js +13 -2
- package/dist/src/core/catalog.js.map +1 -1
- package/dist/src/core/contextPrune.d.ts +13 -0
- package/dist/src/core/contextPrune.js +61 -0
- package/dist/src/core/contextPrune.js.map +1 -0
- package/dist/src/core/httpChatBackend.js +22 -5
- package/dist/src/core/httpChatBackend.js.map +1 -1
- package/dist/src/core/runChat.d.ts +9 -5
- package/dist/src/core/runChat.js +10 -7
- package/dist/src/core/runChat.js.map +1 -1
- package/dist/src/core/session.d.ts +15 -0
- package/dist/src/core/session.js +29 -1
- package/dist/src/core/session.js.map +1 -1
- package/dist/src/core/toolRegistry.d.ts +14 -3
- package/dist/src/core/toolRegistry.js +24 -15
- package/dist/src/core/toolRegistry.js.map +1 -1
- package/dist/src/core/turnLoop.d.ts +4 -0
- package/dist/src/core/turnLoop.js +62 -13
- package/dist/src/core/turnLoop.js.map +1 -1
- package/dist/src/core/types.d.ts +6 -0
- package/dist/src/core/updateCheck.d.ts +46 -0
- package/dist/src/core/updateCheck.js +139 -0
- package/dist/src/core/updateCheck.js.map +1 -0
- package/dist/src/exec/media.d.ts +32 -0
- package/dist/src/exec/media.js +70 -0
- package/dist/src/exec/media.js.map +1 -0
- package/dist/src/exec/output.d.ts +16 -0
- package/dist/src/exec/output.js +41 -0
- package/dist/src/exec/output.js.map +1 -1
- package/dist/src/fs/globTool.d.ts +8 -0
- package/dist/src/fs/globTool.js +117 -0
- package/dist/src/fs/globTool.js.map +1 -0
- package/dist/src/fs/grepTool.d.ts +4 -0
- package/dist/src/fs/grepTool.js +146 -0
- package/dist/src/fs/grepTool.js.map +1 -0
- package/dist/src/fs/index.d.ts +8 -0
- package/dist/src/fs/index.js +13 -0
- package/dist/src/fs/index.js.map +1 -0
- package/dist/src/fs/limits.d.ts +34 -0
- package/dist/src/fs/limits.js +74 -0
- package/dist/src/fs/limits.js.map +1 -0
- package/dist/src/fs/listDirTool.d.ts +2 -0
- package/dist/src/fs/listDirTool.js +68 -0
- package/dist/src/fs/listDirTool.js.map +1 -0
- package/dist/src/fs/readFileTool.d.ts +2 -0
- package/dist/src/fs/readFileTool.js +102 -0
- package/dist/src/fs/readFileTool.js.map +1 -0
- package/dist/src/fs/walk.d.ts +11 -0
- package/dist/src/fs/walk.js +38 -0
- package/dist/src/fs/walk.js.map +1 -0
- package/dist/src/fs/workspaceGuard.d.ts +27 -0
- package/dist/src/fs/workspaceGuard.js +96 -0
- package/dist/src/fs/workspaceGuard.js.map +1 -0
- package/dist/src/index.js +47 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/mediaTools.d.ts +63 -0
- package/dist/src/mcp/mediaTools.js +385 -0
- package/dist/src/mcp/mediaTools.js.map +1 -0
- package/dist/src/mcp/server.d.ts +6 -2
- package/dist/src/mcp/server.js +19 -9
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/media/aiApiClient.d.ts +5 -0
- package/dist/src/media/aiApiClient.js +12 -1
- package/dist/src/media/aiApiClient.js.map +1 -1
- package/dist/src/media/budget.d.ts +42 -0
- package/dist/src/media/budget.js +95 -0
- package/dist/src/media/budget.js.map +1 -0
- package/dist/src/media/executor.d.ts +9 -0
- package/dist/src/media/executor.js +1 -1
- package/dist/src/media/executor.js.map +1 -1
- package/dist/src/media/generateImageTool.d.ts +20 -2
- package/dist/src/media/generateImageTool.js +102 -2
- package/dist/src/media/generateImageTool.js.map +1 -1
- package/dist/src/media/promptSanitizer.d.ts +11 -0
- package/dist/src/media/promptSanitizer.js +33 -0
- package/dist/src/media/promptSanitizer.js.map +1 -0
- package/dist/src/media/taskManager.d.ts +8 -1
- package/dist/src/media/taskManager.js +1 -1
- package/dist/src/media/taskManager.js.map +1 -1
- package/dist/src/tui/App.js +38 -8
- package/dist/src/tui/App.js.map +1 -1
- package/dist/src/tui/ParamEditor.d.ts +11 -2
- package/dist/src/tui/ParamEditor.js +48 -3
- package/dist/src/tui/ParamEditor.js.map +1 -1
- package/dist/src/tui/atPopup.d.ts +8 -0
- package/dist/src/tui/atPopup.js +1 -1
- package/dist/src/tui/atPopup.js.map +1 -1
- package/dist/src/tui/attachments.d.ts +12 -0
- package/dist/src/tui/attachments.js +12 -1
- package/dist/src/tui/attachments.js.map +1 -1
- package/dist/src/tui/chatController.d.ts +73 -2
- package/dist/src/tui/chatController.js +272 -43
- package/dist/src/tui/chatController.js.map +1 -1
- package/dist/src/tui/models.d.ts +6 -0
- package/dist/src/tui/models.js +14 -6
- package/dist/src/tui/models.js.map +1 -1
- package/dist/src/tui/run.js +55 -16
- package/dist/src/tui/run.js.map +1 -1
- package/dist/src/util/brokenPipe.d.ts +10 -0
- package/dist/src/util/brokenPipe.js +19 -0
- package/dist/src/util/brokenPipe.js.map +1 -0
- package/package.json +3 -2
- package/skills/globalgpt/SKILL.md +94 -0
- package/skills/globalgpt/references/troubleshooting.md +34 -0
- package/skills/globalgpt-coding/SKILL.md +65 -0
- package/skills/globalgpt-coding/references/troubleshooting.md +34 -0
- package/skills/globalgpt-image/SKILL.md +109 -0
- package/skills/globalgpt-image/references/recipes.md +52 -0
- package/skills/globalgpt-image/references/troubleshooting.md +49 -0
- package/skills/globalgpt-video/SKILL.md +109 -0
- package/skills/globalgpt-video/references/recipes.md +50 -0
- package/skills/globalgpt-video/references/troubleshooting.md +49 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { getConfigValueSync } from '../config/configFile.js';
|
|
2
|
+
import { DEFAULT_SESSION_MAX_CREDITS } from '../fs/limits.js';
|
|
3
|
+
import { GenerationExecutor } from './executor.js';
|
|
4
|
+
/**
|
|
5
|
+
* Session-cumulative credit budget for the unattended surfaces (MCP server, exec).
|
|
6
|
+
* One instance per process; TUI generations are human-confirmed and do not use it.
|
|
7
|
+
* Reservations happen at the confirm gate (authoritative backend estimate in hand)
|
|
8
|
+
* and are released if the submit then fails — see BudgetConfirmer (added in Task 2).
|
|
9
|
+
*/
|
|
10
|
+
export class SessionBudget {
|
|
11
|
+
cap;
|
|
12
|
+
spentCredits = 0;
|
|
13
|
+
constructor(cap) {
|
|
14
|
+
this.cap = cap;
|
|
15
|
+
}
|
|
16
|
+
get spent() {
|
|
17
|
+
return this.spentCredits;
|
|
18
|
+
}
|
|
19
|
+
/** Atomically check-and-add. False (and no change) when the cap would be crossed. */
|
|
20
|
+
tryReserve(credits) {
|
|
21
|
+
if (this.spentCredits + credits > this.cap)
|
|
22
|
+
return false;
|
|
23
|
+
this.spentCredits += credits;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
release(credits) {
|
|
27
|
+
this.spentCredits = Math.max(0, this.spentCredits - credits);
|
|
28
|
+
}
|
|
29
|
+
describe() {
|
|
30
|
+
return `${this.spentCredits} of ${this.cap} credits spent this session`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Cap precedence: explicit flag > GLBGPT_MAX_CREDITS env > config 'max-credits' > default.
|
|
35
|
+
*
|
|
36
|
+
* `0` is a valid flag value meaning "no media spending this session" (a SessionBudget with
|
|
37
|
+
* cap 0 rejects every reserve); the CLI rejects NaN/negative flag values before they ever
|
|
38
|
+
* reach here (usage error, not a silent fallback). Garbage from env/config still falls back
|
|
39
|
+
* to the default — those aren't user-typed-right-now input, so failing soft is preferable.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveMaxCredits(flag) {
|
|
42
|
+
if (typeof flag === 'number' && Number.isFinite(flag) && flag >= 0)
|
|
43
|
+
return Math.floor(flag);
|
|
44
|
+
const raw = process.env.GLBGPT_MAX_CREDITS ?? getConfigValueSync('max-credits');
|
|
45
|
+
const n = raw !== undefined ? Number.parseInt(raw, 10) : Number.NaN;
|
|
46
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_SESSION_MAX_CREDITS;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The unattended confirm gate: reserve the estimated credits or reject with an
|
|
50
|
+
* actionable message. One instance per execute() call (budgetedExecutor) so a
|
|
51
|
+
* failed submit can roll back exactly its own reservation.
|
|
52
|
+
*/
|
|
53
|
+
export class BudgetConfirmer {
|
|
54
|
+
budget;
|
|
55
|
+
reserved = 0;
|
|
56
|
+
constructor(budget) {
|
|
57
|
+
this.budget = budget;
|
|
58
|
+
}
|
|
59
|
+
async confirm(info) {
|
|
60
|
+
if (this.budget.tryReserve(info.credits)) {
|
|
61
|
+
this.reserved = info.credits;
|
|
62
|
+
return { action: 'generate' };
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
action: 'cancel',
|
|
66
|
+
reason: `Budget exceeded: this generation (~${info.credits} credits) would bring session spend to ` +
|
|
67
|
+
`${this.budget.spent + info.credits} of ${this.budget.cap} credits. Ask the user to restart ` +
|
|
68
|
+
`with --max-credits <n> or run 'glbgpt config set max-credits <n>'.`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Undo the reservation after a failed submit (the money was never spent). */
|
|
72
|
+
rollback() {
|
|
73
|
+
if (this.reserved > 0) {
|
|
74
|
+
this.budget.release(this.reserved);
|
|
75
|
+
this.reserved = 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Budget-gated executor for unattended surfaces: fresh gate per call, rolled back on a thrown submit. */
|
|
80
|
+
export function budgetedExecutor(client, budget) {
|
|
81
|
+
return {
|
|
82
|
+
async execute(modelId, params) {
|
|
83
|
+
const gate = new BudgetConfirmer(budget);
|
|
84
|
+
const executor = new GenerationExecutor(client, gate);
|
|
85
|
+
try {
|
|
86
|
+
return await executor.execute(modelId, params);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
gate.rollback();
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../../../src/media/budget.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,7 @@ export type ConfirmDecision = {
|
|
|
17
17
|
action: 'generate';
|
|
18
18
|
} | {
|
|
19
19
|
action: 'cancel';
|
|
20
|
+
reason?: string;
|
|
20
21
|
} | {
|
|
21
22
|
action: 'edit';
|
|
22
23
|
model: string;
|
|
@@ -34,10 +35,18 @@ export type ExecuteOutcome = {
|
|
|
34
35
|
params: MediaGenParams;
|
|
35
36
|
} | {
|
|
36
37
|
status: 'cancelled';
|
|
38
|
+
reason?: string;
|
|
37
39
|
} | {
|
|
38
40
|
status: 'not_sellable';
|
|
39
41
|
reasonCode: string | null;
|
|
40
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* The executor surface the media tools depend on. GenerationExecutor implements it
|
|
45
|
+
* structurally; budget wrappers (budgetedExecutor) implement it too.
|
|
46
|
+
*/
|
|
47
|
+
export interface MediaExecutor {
|
|
48
|
+
execute(modelId: string, rawParams: MediaGenParams): Promise<ExecuteOutcome>;
|
|
49
|
+
}
|
|
41
50
|
/**
|
|
42
51
|
* Shared generation pipeline: validate → estimate → confirm → upload local images → submit.
|
|
43
52
|
*
|
|
@@ -39,7 +39,7 @@ export class GenerationExecutor {
|
|
|
39
39
|
credits,
|
|
40
40
|
});
|
|
41
41
|
if (decision.action === 'cancel') {
|
|
42
|
-
return { status: 'cancelled' };
|
|
42
|
+
return decision.reason ? { status: 'cancelled', reason: decision.reason } : { status: 'cancelled' };
|
|
43
43
|
}
|
|
44
44
|
if (decision.action === 'edit') {
|
|
45
45
|
model = decision.model;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../src/media/executor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAA6B,oBAAoB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../src/media/executor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAA6B,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAuC7E;;;;;GAKG;AACH,MAAM,OAAO,kBAAkB;IAEV;IACA;IAFnB,YACmB,MAAmB,EACnB,SAAoB;QADpB,WAAM,GAAN,MAAM,CAAa;QACnB,cAAS,GAAT,SAAS,CAAW;IACpC,CAAC;IAEJ,4FAA4F;IAC5F,KAAK,CAAC,QAAQ,CACZ,OAAe,EACf,SAAyB;QAEzB,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC;QACzE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1F,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;IACpG,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe,EAAE,SAAyB;QACtD,IAAI,KAAK,GAAG,OAAO,CAAC;QACpB,IAAI,SAAS,GAAG,SAAS,CAAC;QAC1B,6FAA6F;QAC7F,uCAAuC;QACvC,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YAC9F,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC;YAChD,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC5C,KAAK,EAAE,IAAI,CAAC,EAAE;gBACd,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM;gBACN,OAAO;aACR,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACjC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;YACtG,CAAC;YACD,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC/B,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;gBACvB,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC;gBAC5B,SAAS;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC5D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;YACnF,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACpF,CAAC;IACH,CAAC;IAED,yFAAyF;IACjF,KAAK,CAAC,iBAAiB,CAAC,IAAe,EAAE,MAAsB;QACrE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QAChE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAC7G,CAAC;QACF,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACzC,CAAC;CACF;AAED,SAAS,KAAK,CAAC,CAAS;IACtB,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import type { ClientTool } from '../core/types.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { MediaExecutor } from './executor.js';
|
|
3
3
|
import type { TaskManager } from './taskManager.js';
|
|
4
4
|
import type { TaskLedger } from './taskLedger.js';
|
|
5
5
|
export interface MediaToolDeps {
|
|
6
|
-
executor:
|
|
6
|
+
executor: MediaExecutor;
|
|
7
7
|
taskManager: TaskManager;
|
|
8
8
|
ledger: TaskLedger;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a reference-image value the model passed (a prior generation's local path, an http
|
|
11
|
+
* URL, or "last") into a submittable value. Returns undefined only when "last" was requested
|
|
12
|
+
* but nothing has been generated yet. When omitted, references pass through unchanged.
|
|
13
|
+
*/
|
|
14
|
+
resolveReference?: (ref: string) => string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Images the user attached to THIS message (hosted URLs / local paths). When the user attaches
|
|
17
|
+
* an image and asks to build on it, these are used as the reference even if the model didn't
|
|
18
|
+
* pass one — the confirmation shows them so a false positive is caught before spending.
|
|
19
|
+
*/
|
|
20
|
+
pendingReferences?: () => string[];
|
|
21
|
+
/**
|
|
22
|
+
* Every media reference this session knows about (generated output URLs and their local paths).
|
|
23
|
+
* Used to move a reference the model pasted INTO THE PROMPT TEXT back into the reference
|
|
24
|
+
* parameter. Exact-match only — links that are not session-known media are never touched.
|
|
25
|
+
*/
|
|
26
|
+
knownMediaRefs?: () => string[];
|
|
9
27
|
}
|
|
10
28
|
/** Back-compat alias. */
|
|
11
29
|
export type GenerateImageToolDeps = MediaToolDeps;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_IMAGE_MODEL, DEFAULT_VIDEO_MODEL, IMAGE_MODELS, VIDEO_MODELS, findModel } from './manifest.js';
|
|
2
2
|
import { PaymentRequiredError } from './aiApiClient.js';
|
|
3
3
|
import { describePaymentRequired, describeNotSellable } from './errors.js';
|
|
4
|
+
import { extractKnownRefs } from './promptSanitizer.js';
|
|
4
5
|
/**
|
|
5
6
|
* The real in-chat media tools (contract §4): estimate → confirm (via the executor's Confirmer) →
|
|
6
7
|
* submit, then return the task id to the model immediately. The TaskManager polls and downloads in
|
|
@@ -26,6 +27,11 @@ function buildTool(kind, deps) {
|
|
|
26
27
|
seed: { type: 'integer', description: 'Set only if the user gives a seed' },
|
|
27
28
|
negative_prompt: { type: 'string', description: 'Set only if the user says what to avoid' },
|
|
28
29
|
};
|
|
30
|
+
const refHint = 'Reference an image by the local path shown in a prior generation ("Saved locally at …"), ' +
|
|
31
|
+
'an http(s) URL, or "last" for the most recently generated image. Only set when the user wants ' +
|
|
32
|
+
'to build on an existing image; not all models support it. Pass references ONLY via this ' +
|
|
33
|
+
'parameter — never write an image URL or file path into the prompt text (the prompt is literal ' +
|
|
34
|
+
'text for the media model, which cannot fetch links from it).';
|
|
29
35
|
if (isVideo) {
|
|
30
36
|
properties.duration = { type: 'integer', description: 'Video length in seconds — set only if the user asks' };
|
|
31
37
|
properties.generate_audio = {
|
|
@@ -33,13 +39,25 @@ function buildTool(kind, deps) {
|
|
|
33
39
|
description: 'Sound on/off. Set ONLY if the user explicitly asks for it. Omit otherwise — each model ' +
|
|
34
40
|
'uses its own default, which you do not know. Do not assume the result has or lacks sound.',
|
|
35
41
|
};
|
|
42
|
+
properties.start_image = { type: 'string', description: `First frame for image-to-video. ${refHint}` };
|
|
43
|
+
properties.end_image = { type: 'string', description: `Last frame (with start_image). ${refHint}` };
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
properties.reference_images = {
|
|
47
|
+
type: 'array',
|
|
48
|
+
items: { type: 'string' },
|
|
49
|
+
description: `Images to build on / edit (image-to-image). ${refHint}`,
|
|
50
|
+
};
|
|
36
51
|
}
|
|
37
52
|
// NOTE: multi-image `count`/`n` is intentionally NOT exposed — the download path handles a
|
|
38
53
|
// single output, so a count>1 would be charged per image but only one delivered. Re-add once
|
|
39
54
|
// multi-output (N URLs + downloading/showing all N) is wired end to end.
|
|
40
55
|
return {
|
|
41
56
|
name: isVideo ? 'generate_video' : 'generate_image',
|
|
42
|
-
description: `Generate ${isVideo ? 'a video' : 'an image'} from a text prompt.
|
|
57
|
+
description: `Generate ${isVideo ? 'a video' : 'an image'} from a text prompt. When the user asks for ${isVideo ? 'a video' : 'an image'}, call this tool immediately — anything you say first may only state intent (e.g. "I'll generate ` +
|
|
58
|
+
`that now"). NEVER announce, estimate, or guess costs, prices, credits, or settings beforehand: ` +
|
|
59
|
+
`you do not know them, and the system already displays an authoritative cost estimate with a ` +
|
|
60
|
+
`confirmation dialog as part of this call. Returns a task id immediately; ` +
|
|
43
61
|
`the ${noun} runs in the background and appears in the conversation when ready (do not wait for it). ` +
|
|
44
62
|
`After calling, tell the user generation has STARTED — never claim the ${noun} is already done. ` +
|
|
45
63
|
`Set a parameter ONLY when the user explicitly asked for it; otherwise omit it and the system ` +
|
|
@@ -47,9 +65,16 @@ function buildTool(kind, deps) {
|
|
|
47
65
|
`user which settings were used or predict any property of the result.`,
|
|
48
66
|
parameters: { type: 'object', properties, required: ['prompt'] },
|
|
49
67
|
async execute(args) {
|
|
50
|
-
|
|
68
|
+
let prompt = typeof args.prompt === 'string' ? args.prompt : '';
|
|
51
69
|
if (!prompt.trim())
|
|
52
70
|
return { error: 'prompt is required' };
|
|
71
|
+
// Models sometimes paste a prior output's URL into the prompt instead of the reference
|
|
72
|
+
// parameter — move it back where the media model can actually receive it.
|
|
73
|
+
let promptRefs = [];
|
|
74
|
+
const known = deps.knownMediaRefs?.() ?? [];
|
|
75
|
+
if (known.length > 0) {
|
|
76
|
+
({ prompt, refs: promptRefs } = extractKnownRefs(prompt, known));
|
|
77
|
+
}
|
|
53
78
|
const modelId = typeof args.model === 'string' && args.model ? args.model : defaultModel;
|
|
54
79
|
const params = {
|
|
55
80
|
prompt,
|
|
@@ -60,8 +85,26 @@ function buildTool(kind, deps) {
|
|
|
60
85
|
params.seed = args.seed;
|
|
61
86
|
if (typeof args.negative_prompt === 'string' && args.negative_prompt)
|
|
62
87
|
params.negativePrompt = args.negative_prompt;
|
|
88
|
+
// Resolve reference images ("last" / generated path / URL) into submittable values.
|
|
89
|
+
const resolveRef = (ref) => {
|
|
90
|
+
if (typeof ref !== 'string' || !ref.trim())
|
|
91
|
+
return undefined;
|
|
92
|
+
const resolved = deps.resolveReference ? deps.resolveReference(ref) : ref;
|
|
93
|
+
return resolved ?? null; // null = "last" requested but nothing generated yet
|
|
94
|
+
};
|
|
95
|
+
const noLastImage = 'No previously generated image to reference yet — generate one first.';
|
|
63
96
|
const warnings = [];
|
|
64
97
|
if (isVideo) {
|
|
98
|
+
const start = resolveRef(args.start_image);
|
|
99
|
+
if (start === null)
|
|
100
|
+
return { error: noLastImage };
|
|
101
|
+
if (start)
|
|
102
|
+
params.startImage = start;
|
|
103
|
+
const end = resolveRef(args.end_image);
|
|
104
|
+
if (end === null)
|
|
105
|
+
return { error: noLastImage };
|
|
106
|
+
if (end)
|
|
107
|
+
params.endImage = end;
|
|
65
108
|
if (typeof args.duration === 'number')
|
|
66
109
|
params.duration = args.duration;
|
|
67
110
|
if (typeof args.generate_audio === 'boolean') {
|
|
@@ -80,9 +123,66 @@ function buildTool(kind, deps) {
|
|
|
80
123
|
}
|
|
81
124
|
}
|
|
82
125
|
}
|
|
126
|
+
else if (Array.isArray(args.reference_images) && args.reference_images.length > 0) {
|
|
127
|
+
const refs = [];
|
|
128
|
+
for (const ref of args.reference_images) {
|
|
129
|
+
const resolved = resolveRef(ref);
|
|
130
|
+
if (resolved === null)
|
|
131
|
+
return { error: noLastImage };
|
|
132
|
+
if (resolved)
|
|
133
|
+
refs.push(resolved);
|
|
134
|
+
}
|
|
135
|
+
if (refs.length > 0)
|
|
136
|
+
params.images = refs;
|
|
137
|
+
}
|
|
138
|
+
// References extracted from the prompt text fill still-empty slots (an explicit parameter
|
|
139
|
+
// wins; a duplicate of it is just dropped). Resolved like any other ref so a local output
|
|
140
|
+
// path becomes its hosted URL.
|
|
141
|
+
if (promptRefs.length > 0) {
|
|
142
|
+
const used = new Set([params.startImage, params.endImage, ...(params.images ?? [])].filter(Boolean));
|
|
143
|
+
const fresh = promptRefs
|
|
144
|
+
.map((r) => resolveRef(r))
|
|
145
|
+
.filter((r) => typeof r === 'string' && !used.has(r));
|
|
146
|
+
let moved = false;
|
|
147
|
+
if (isVideo) {
|
|
148
|
+
for (const r of fresh) {
|
|
149
|
+
if (!params.startImage) {
|
|
150
|
+
params.startImage = r;
|
|
151
|
+
moved = true;
|
|
152
|
+
}
|
|
153
|
+
else if (!params.endImage && r !== params.startImage) {
|
|
154
|
+
params.endImage = r;
|
|
155
|
+
moved = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if ((!params.images || params.images.length === 0) && fresh.length > 0) {
|
|
160
|
+
params.images = fresh;
|
|
161
|
+
moved = true;
|
|
162
|
+
}
|
|
163
|
+
if (moved) {
|
|
164
|
+
warnings.push('A generated image reference was found INSIDE the prompt text and was moved to the ' +
|
|
165
|
+
`${isVideo ? 'start_image' : 'reference_images'} parameter (the media model cannot read links ` +
|
|
166
|
+
'from prompt text). Next time pass references only via the parameter.');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Auto-reference: the user attached an image and asked to build on it, but the model passed
|
|
170
|
+
// no explicit reference — use the attachment. The confirmation lists it so a false positive
|
|
171
|
+
// (an unrelated attached image) is caught before spending.
|
|
172
|
+
const attached = deps.pendingReferences?.() ?? [];
|
|
173
|
+
if (attached.length > 0) {
|
|
174
|
+
if (isVideo && !params.startImage)
|
|
175
|
+
params.startImage = attached[0];
|
|
176
|
+
else if (!isVideo && (!params.images || params.images.length === 0))
|
|
177
|
+
params.images = attached;
|
|
178
|
+
}
|
|
83
179
|
try {
|
|
84
180
|
const outcome = await deps.executor.execute(modelId, params);
|
|
85
181
|
if (outcome.status === 'cancelled') {
|
|
182
|
+
// A reasoned cancel is a policy gate (e.g. session budget), not a user decline —
|
|
183
|
+
// report it as an error so the agent relays it instead of "the user declined".
|
|
184
|
+
if (outcome.reason)
|
|
185
|
+
return { error: outcome.reason };
|
|
86
186
|
return {
|
|
87
187
|
cancelled: true,
|
|
88
188
|
note: 'The user declined this generation (they may adjust the parameters and retry).',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateImageTool.js","sourceRoot":"","sources":["../../../src/media/generateImageTool.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEhH,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"generateImageTool.js","sourceRoot":"","sources":["../../../src/media/generateImageTool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moves session-known media references (generated output URLs / local paths) out of a generation
|
|
3
|
+
* prompt's text. Models sometimes paste a prior output's URL into the prompt instead of the
|
|
4
|
+
* reference parameter — the media model then never receives the reference and may even render the
|
|
5
|
+
* URL as literal text. Matching is EXACT against the known list only: any other link the user
|
|
6
|
+
* wrote (a website, an external image, …) is left byte-for-byte untouched by design.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractKnownRefs(prompt: string, known: string[]): {
|
|
9
|
+
prompt: string;
|
|
10
|
+
refs: string[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moves session-known media references (generated output URLs / local paths) out of a generation
|
|
3
|
+
* prompt's text. Models sometimes paste a prior output's URL into the prompt instead of the
|
|
4
|
+
* reference parameter — the media model then never receives the reference and may even render the
|
|
5
|
+
* URL as literal text. Matching is EXACT against the known list only: any other link the user
|
|
6
|
+
* wrote (a website, an external image, …) is left byte-for-byte untouched by design.
|
|
7
|
+
*/
|
|
8
|
+
/** A "参考图:" / "reference image:" style label directly before an extracted ref. */
|
|
9
|
+
const LABEL_BEFORE = /(?:参考图片?|reference(?:\s+images?)?|image)\s*[::]\s*$/i;
|
|
10
|
+
export function extractKnownRefs(prompt, known) {
|
|
11
|
+
const refs = [];
|
|
12
|
+
let out = prompt;
|
|
13
|
+
// Longest first, so a ref that contains another as a prefix is matched whole.
|
|
14
|
+
const candidates = [...new Set(known.filter(Boolean))].sort((a, b) => b.length - a.length);
|
|
15
|
+
for (const ref of candidates) {
|
|
16
|
+
if (!out.includes(ref))
|
|
17
|
+
continue;
|
|
18
|
+
refs.push(ref);
|
|
19
|
+
let idx;
|
|
20
|
+
while ((idx = out.indexOf(ref)) !== -1) {
|
|
21
|
+
const label = LABEL_BEFORE.exec(out.slice(0, idx));
|
|
22
|
+
const start = label ? idx - label[0].length : idx;
|
|
23
|
+
out = out.slice(0, start) + out.slice(idx + ref.length);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (refs.length === 0)
|
|
27
|
+
return { prompt, refs };
|
|
28
|
+
const cleaned = out.replace(/[ \t]{2,}/g, ' ').trim();
|
|
29
|
+
// A prompt that was ONLY the reference would become empty — keep the original text then,
|
|
30
|
+
// since the media model still needs some prompt.
|
|
31
|
+
return { prompt: cleaned.length > 0 ? cleaned : prompt, refs };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=promptSanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptSanitizer.js","sourceRoot":"","sources":["../../../src/media/promptSanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,kFAAkF;AAClF,MAAM,YAAY,GAAG,sDAAsD,CAAC;AAE5E,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,KAAe;IAC9D,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,GAAG,GAAG,MAAM,CAAC;IACjB,8EAA8E;IAC9E,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3F,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QACjC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,IAAI,GAAW,CAAC;QAChB,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YACnD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;YAClD,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,yFAAyF;IACzF,iDAAiD;IACjD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;AACjE,CAAC"}
|
|
@@ -10,10 +10,17 @@ export type TaskEvent = {
|
|
|
10
10
|
localPath: string;
|
|
11
11
|
outputUrl: string;
|
|
12
12
|
mediaType?: number | null;
|
|
13
|
-
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `timedOut: true` marks OUR poll loop giving up, not an authoritative backend
|
|
16
|
+
* verdict — the task may still be running and may still succeed. Consumers must
|
|
17
|
+
* not cache it as a terminal failure (MCP check_task treats it as "still generating").
|
|
18
|
+
*/
|
|
19
|
+
| {
|
|
14
20
|
type: 'task_failed';
|
|
15
21
|
taskId: string;
|
|
16
22
|
reason: string;
|
|
23
|
+
timedOut?: boolean;
|
|
17
24
|
};
|
|
18
25
|
export interface TaskMeta {
|
|
19
26
|
model?: string;
|
|
@@ -59,7 +59,7 @@ export class TaskManager {
|
|
|
59
59
|
const elapsedMs = Date.now() - start;
|
|
60
60
|
this.emit({ type: 'task_progress', taskId, elapsedMs, status: info.status });
|
|
61
61
|
if (elapsedMs >= this.timeoutMs) {
|
|
62
|
-
return this.emit({ type: 'task_failed', taskId, reason: 'Timed out waiting for the task.' });
|
|
62
|
+
return this.emit({ type: 'task_failed', taskId, reason: 'Timed out waiting for the task.', timedOut: true });
|
|
63
63
|
}
|
|
64
64
|
await this.sleep(this.pollMs);
|
|
65
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"taskManager.js","sourceRoot":"","sources":["../../../src/media/taskManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"taskManager.js","sourceRoot":"","sources":["../../../src/media/taskManager.ts"],"names":[],"mappings":"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"}
|
package/dist/src/tui/App.js
CHANGED
|
@@ -5,13 +5,14 @@ import wrapAnsi from 'wrap-ansi';
|
|
|
5
5
|
import { MultilineInput } from './MultilineInput.js';
|
|
6
6
|
import { getChatModels, modelDisplay } from './models.js';
|
|
7
7
|
import { findModel } from '../media/manifest.js';
|
|
8
|
-
import { ParamEditor } from './ParamEditor.js';
|
|
8
|
+
import { ParamEditor, displayMediaRef } from './ParamEditor.js';
|
|
9
9
|
import { filterCommands, SLASH_COMMANDS } from './commands.js';
|
|
10
10
|
import { LOGO_FRAMES, TAGLINE, brandTitle, supportsTruecolor } from './branding.js';
|
|
11
11
|
import { applyMarkdown } from './markdown.js';
|
|
12
12
|
import { hyperlink, fileUrl } from '../util/hyperlink.js';
|
|
13
13
|
import { atCandidates } from './atPopup.js';
|
|
14
14
|
import { readClipboardImage } from '../util/clipboardImage.js';
|
|
15
|
+
import { MAX_TOOL_CALLS_PER_TURN } from '../fs/index.js';
|
|
15
16
|
/**
|
|
16
17
|
* Chat TUI (phase 1). A thin view over {@link ChatController}: it renders the timeline,
|
|
17
18
|
* handles slash commands and the `/model` selector, and forwards user input to the
|
|
@@ -78,7 +79,7 @@ export function App({ controller, confirmationBridge, sessionPicker, firstRun, f
|
|
|
78
79
|
return;
|
|
79
80
|
}
|
|
80
81
|
let alive = true;
|
|
81
|
-
void atCandidates(atPartial).then((c) => {
|
|
82
|
+
void atCandidates(atPartial, { hasLastImage: controller.resolveMediaReference('last') !== undefined }).then((c) => {
|
|
82
83
|
if (alive) {
|
|
83
84
|
setAtCands(c);
|
|
84
85
|
setAtIndex(0);
|
|
@@ -388,13 +389,13 @@ export function App({ controller, confirmationBridge, sessionPicker, firstRun, f
|
|
|
388
389
|
controller.note(`Unknown command: /${cmd}`);
|
|
389
390
|
}
|
|
390
391
|
};
|
|
391
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: staticItems, children: (item, i) => item === 'welcome' ? (_jsx(WelcomeBlock, { model: state.model, accountLine: accountLine, firstRun: firstRun ?? false, settled: true }, "welcome")) : (_jsx(TimelineRow, { item: item, columns: columns }, i)) }, staticEpoch), !conversationStarted && (_jsx(WelcomeBlock, { model: state.model, accountLine: accountLine, firstRun: firstRun ?? false })), liveItems.map((item, i) => (_jsx(TimelineRow, { item: item, columns: columns, awaitingConfirm: !!pending }, frozen + i))), state.busy && !pending && _jsx(BusySpinner, {}), state.runningTasks.length > 0 && _jsx(TaskSummaryBar, { tasks: state.runningTasks }), pending && editingParams ? (_jsx(ParamEditor, { info: pending, columns: columns, onApply: (model, params) => confirmationBridge?.resolve({ action: 'edit', model, params }), onCancel: () => setEditingParams(false) })) : pending ? (_jsx(ConfirmationBlock, { info: pending })) : sessions ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "Resume a conversation (\u2191/\u2193, Enter to choose, Esc to cancel):" }), sessions.map((s, i) => (_jsxs(Text, { color: i === sessIndex ? 'cyan' : undefined, children: [i === sessIndex ? '❯ ' : ' ', s.preview, " ", _jsxs(Text, { color: "gray", children: ["\u00B7 ", s.model, " \u00B7 ", timeAgo(s.updatedAt)] })] }, s.id)))] })) : selecting ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "Select a model (\u2191/\u2193, Enter to choose, Esc to cancel):" }), getChatModels().map((m, i) => (_jsxs(Text, { color: i === selIndex ? 'cyan' : undefined, children: [i === selIndex ? '❯ ' : ' ', modelDisplay(m)] }, m.id)))] })) : (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [showSuggestions && (_jsxs(Box, { flexDirection: "column", children: [suggestions.map((c, i) => (_jsxs(Text, { color: c === selectedSuggestion ? 'cyan' : undefined, children: [c === selectedSuggestion ? '❯ ' : ' ', "/", c.name, _jsxs(Text, { color: "gray", children: [c.args ? ` ${c.args}` : '', " \u2014 ", c.description] })] }, c.name))), _jsx(Text, { color: "gray", dimColor: true, children: "\u2191/\u2193 select \u00B7 Tab complete \u00B7 Enter run" })] })), showAt && (_jsxs(Box, { flexDirection: "column", children: [atCands.map((c) => (_jsxs(Text, { color: c === selectedAt ? 'cyan' : undefined, children: [c === selectedAt ? '❯ ' : ' ', c.label, _jsxs(Text, { color: "gray", children: [" \u2014 ", c.hint] })] }, c.label))), _jsx(Text, { color: "gray", dimColor: true, children: "\u2191/\u2193 select \u00B7 Tab/Enter complete" })] })), state.queued.map((q, i) => (_jsxs(Text, { color: "gray", dimColor: true, children: [' ↳ queued: ', q.display] }, i))), _jsxs(Box, { borderStyle: "round", borderLeft: false, borderRight: false, borderColor: "gray", paddingLeft: 1, children: [_jsx(Text, { color: "cyan", children: '› ' }), _jsx(MultilineInput, { value: input, onChange: onInputChange, onSubmit: onSubmit, onHistoryUp: onHistoryUp, onHistoryDown: onHistoryDown, recallActive: recallActive, columns: Math.max(20, columns - 4), verticalNav: !showAt && !showSuggestions, placeholder: 'Type a message, or /help for commands (\\ + Enter for a new line)' })] }), _jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: exitArmed ? 'yellow' : 'gray', dimColor: !exitArmed, children: exitArmed
|
|
392
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: staticItems, children: (item, i) => item === 'welcome' ? (_jsx(WelcomeBlock, { model: state.model, accountLine: accountLine, firstRun: firstRun ?? false, settled: true }, "welcome")) : (_jsx(TimelineRow, { item: item, columns: columns }, i)) }, staticEpoch), !conversationStarted && (_jsx(WelcomeBlock, { model: state.model, accountLine: accountLine, firstRun: firstRun ?? false })), liveItems.map((item, i) => (_jsx(TimelineRow, { item: item, columns: columns, awaitingConfirm: !!pending }, frozen + i))), state.busy && !pending && _jsx(BusySpinner, {}), state.runningTasks.length > 0 && _jsx(TaskSummaryBar, { tasks: state.runningTasks }), pending && editingParams ? (_jsx(ParamEditor, { info: pending, columns: columns, onApply: (model, params) => confirmationBridge?.resolve({ action: 'edit', model, params }), onCancel: () => setEditingParams(false), nameFor: (r) => controller.localNameForUrl(r) })) : pending ? (_jsx(ConfirmationBlock, { info: pending, nameFor: (r) => controller.localNameForUrl(r) })) : sessions ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "Resume a conversation (\u2191/\u2193, Enter to choose, Esc to cancel):" }), sessions.map((s, i) => (_jsxs(Text, { color: i === sessIndex ? 'cyan' : undefined, children: [i === sessIndex ? '❯ ' : ' ', s.preview, " ", _jsxs(Text, { color: "gray", children: ["\u00B7 ", s.model, " \u00B7 ", timeAgo(s.updatedAt)] })] }, s.id)))] })) : selecting ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "Select a model (\u2191/\u2193, Enter to choose, Esc to cancel):" }), getChatModels().map((m, i) => (_jsxs(Text, { color: i === selIndex ? 'cyan' : undefined, children: [i === selIndex ? '❯ ' : ' ', modelDisplay(m)] }, m.id)))] })) : (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [showSuggestions && (_jsxs(Box, { flexDirection: "column", children: [suggestions.map((c, i) => (_jsxs(Text, { color: c === selectedSuggestion ? 'cyan' : undefined, children: [c === selectedSuggestion ? '❯ ' : ' ', "/", c.name, _jsxs(Text, { color: "gray", children: [c.args ? ` ${c.args}` : '', " \u2014 ", c.description] })] }, c.name))), _jsx(Text, { color: "gray", dimColor: true, children: "\u2191/\u2193 select \u00B7 Tab complete \u00B7 Enter run" })] })), showAt && (_jsxs(Box, { flexDirection: "column", children: [atCands.map((c) => (_jsxs(Text, { color: c === selectedAt ? 'cyan' : undefined, children: [c === selectedAt ? '❯ ' : ' ', c.label, _jsxs(Text, { color: "gray", children: [" \u2014 ", c.hint] })] }, c.label))), _jsx(Text, { color: "gray", dimColor: true, children: "\u2191/\u2193 select \u00B7 Tab/Enter complete" })] })), state.queued.map((q, i) => (_jsxs(Text, { color: "gray", dimColor: true, children: [' ↳ queued: ', q.display] }, i))), _jsxs(Box, { borderStyle: "round", borderLeft: false, borderRight: false, borderColor: "gray", paddingLeft: 1, children: [_jsx(Text, { color: "cyan", children: '› ' }), _jsx(MultilineInput, { value: input, onChange: onInputChange, onSubmit: onSubmit, onHistoryUp: onHistoryUp, onHistoryDown: onHistoryDown, recallActive: recallActive, columns: Math.max(20, columns - 4), verticalNav: !showAt && !showSuggestions, placeholder: 'Type a message, or /help for commands (\\ + Enter for a new line)' })] }), _jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: exitArmed ? 'yellow' : 'gray', dimColor: !exitArmed, children: exitArmed
|
|
392
393
|
? 'Press ctrl+c again to exit'
|
|
393
394
|
: state.busy
|
|
394
395
|
? 'Esc interrupt · Enter to queue'
|
|
395
396
|
: '/ commands · @ attach image' }), _jsxs(Text, { color: "gray", dimColor: true, children: [state.model, state.sessionUsage.promptTokens + state.sessionUsage.completionTokens > 0
|
|
396
397
|
? ` · ${formatTokens(state.sessionUsage.promptTokens + state.sessionUsage.completionTokens)} tok`
|
|
397
|
-
: '', state.sessionUsage.mediaCreditsEstimated > 0 ? ` · ~${state.sessionUsage.mediaCreditsEstimated} cr` : '', state.runningTasks.length > 0 ? ` · ${state.runningTasks.length} task${state.runningTasks.length > 1 ? 's' : ''} running` : ''] })] })] }))] }));
|
|
398
|
+
: '', state.sessionUsage.mediaCreditsEstimated > 0 ? ` · ~${state.sessionUsage.mediaCreditsEstimated} cr` : '', state.runningTasks.length > 0 ? ` · ${state.runningTasks.length} task${state.runningTasks.length > 1 ? 's' : ''} running` : '', state.busy && state.toolCallsUsed > 0 ? (_jsxs(Text, { color: "gray", children: [" \u00B7 \u2692 tools ", state.toolCallsUsed, "/", MAX_TOOL_CALLS_PER_TURN] })) : null] })] })] }))] }));
|
|
398
399
|
}
|
|
399
400
|
/**
|
|
400
401
|
* Full brand welcome area shown until the conversation starts (baseline §2/§3): logo raster
|
|
@@ -437,7 +438,8 @@ function timeAgo(ts) {
|
|
|
437
438
|
* set is shown as-is; an unset value shows the model's default where known, or "model default"
|
|
438
439
|
* where the upstream decides. Audio is always spelled out for video so the user never has to infer.
|
|
439
440
|
*/
|
|
440
|
-
function effectiveSettings(info) {
|
|
441
|
+
function effectiveSettings(info, nameFor) {
|
|
442
|
+
const shortRef = (ref) => displayMediaRef(ref, nameFor);
|
|
441
443
|
const p = info.params;
|
|
442
444
|
const s = findModel(info.model)?.support;
|
|
443
445
|
const isVideo = info.type === 'video';
|
|
@@ -469,10 +471,18 @@ function effectiveSettings(info) {
|
|
|
469
471
|
rows.push(['seed', String(p.seed)]);
|
|
470
472
|
if (p.negativePrompt)
|
|
471
473
|
rows.push(['avoid', p.negativePrompt]);
|
|
474
|
+
// Reference images — show what the generation builds on, so the user sees it (not the model).
|
|
475
|
+
if (p.startImage)
|
|
476
|
+
rows.push(['start frame', shortRef(p.startImage)]);
|
|
477
|
+
if (p.endImage)
|
|
478
|
+
rows.push(['end frame', shortRef(p.endImage)]);
|
|
479
|
+
if (p.images && p.images.length > 0) {
|
|
480
|
+
rows.push(['reference', p.images.length === 1 ? shortRef(p.images[0]) : `${p.images.length} images`]);
|
|
481
|
+
}
|
|
472
482
|
return rows;
|
|
473
483
|
}
|
|
474
|
-
function ConfirmationBlock({ info }) {
|
|
475
|
-
const rows = effectiveSettings(info);
|
|
484
|
+
function ConfirmationBlock({ info, nameFor, }) {
|
|
485
|
+
const rows = effectiveSettings(info, nameFor);
|
|
476
486
|
const labelWidth = Math.max(...rows.map(([label]) => label.length));
|
|
477
487
|
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: "yellow", children: ["Generate ", info.type === 'video' ? 'a video' : 'an image', " with ", info.displayName, "?"] }), _jsxs(Text, { color: "gray", children: [" \u201C", info.params.prompt, "\u201D"] }), _jsxs(Text, { color: "gray", children: [" model", ' ', info.model] }), rows.map(([label, value]) => (_jsxs(Text, { color: "gray", children: [' ', label.padEnd(labelWidth), " ", value] }, label))), _jsxs(Text, { color: "gray", children: [" ~", info.credits, " credits"] }), _jsx(Text, { color: "cyan", children: " [g] Generate [e] Edit params [c] Cancel" })] }));
|
|
478
488
|
}
|
|
@@ -497,6 +507,17 @@ function BusySpinner() {
|
|
|
497
507
|
const DOT = '●';
|
|
498
508
|
/** Dot color: brand coral (from the logo) on truecolor terminals; plain elsewhere. */
|
|
499
509
|
const DOT_COLOR = supportsTruecolor() ? '#F15B66' : undefined;
|
|
510
|
+
/**
|
|
511
|
+
* The one place semantic rich-note styles become colors (see RichSpan in chatController):
|
|
512
|
+
* header = section title (bold, default fg), accent = ids/actions (matches the cyan used
|
|
513
|
+
* for interactive selection), dim = descriptions/hints, warn = attention.
|
|
514
|
+
*/
|
|
515
|
+
const RICH_STYLE_COLORS = {
|
|
516
|
+
header: undefined,
|
|
517
|
+
accent: 'cyan',
|
|
518
|
+
dim: 'gray',
|
|
519
|
+
warn: 'yellow',
|
|
520
|
+
};
|
|
500
521
|
/**
|
|
501
522
|
* Pre-wrap ANSI-styled text to fit beside a fixed indent column. Ink's flex measurement
|
|
502
523
|
* doesn't subtract sibling widths when wrapping <Text>, so long styled lines overflow the
|
|
@@ -537,6 +558,13 @@ function TimelineRow({ item, columns, awaitingConfirm, }) {
|
|
|
537
558
|
}
|
|
538
559
|
case 'note':
|
|
539
560
|
return (_jsx(Box, { children: _jsx(Text, { color: "gray", children: item.text }) }));
|
|
561
|
+
case 'rich':
|
|
562
|
+
return (_jsx(Box, { flexDirection: "column", children: item.lines.map((line, i) => (_jsx(Text, { children: line.length === 0
|
|
563
|
+
? ' '
|
|
564
|
+
: line.map((span, j) => (
|
|
565
|
+
// dim = gray alone (matching plain notes); gray + dimColor stacked is
|
|
566
|
+
// near-invisible on some dark themes.
|
|
567
|
+
_jsx(Text, { bold: span.style === 'header', color: span.style ? RICH_STYLE_COLORS[span.style] : undefined, children: span.text }, j))) }, i))) }));
|
|
540
568
|
case 'error':
|
|
541
569
|
return (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["error: ", item.text] }) }));
|
|
542
570
|
}
|
|
@@ -548,6 +576,8 @@ function summarizeResult(result) {
|
|
|
548
576
|
return `failed: ${result.error}`;
|
|
549
577
|
if (result.cancelled === true)
|
|
550
578
|
return 'cancelled';
|
|
579
|
+
if (typeof result.summary === 'string')
|
|
580
|
+
return result.summary;
|
|
551
581
|
if (typeof result.task_id === 'string') {
|
|
552
582
|
const credits = typeof result.estimated_credits === 'number' ? ` · ~${result.estimated_credits} credits` : '';
|
|
553
583
|
return `task ${result.task_id} submitted${credits} — generating in the background`;
|
|
@@ -567,7 +597,7 @@ function summarizeArgs(args) {
|
|
|
567
597
|
const p = args.prompt;
|
|
568
598
|
bits.push(p.length > 40 ? `${p.slice(0, 39)}…` : p);
|
|
569
599
|
}
|
|
570
|
-
for (const key of ['model', 'aspect_ratio', 'resolution', 'duration']) {
|
|
600
|
+
for (const key of ['model', 'aspect_ratio', 'resolution', 'duration', 'path', 'pattern', 'include']) {
|
|
571
601
|
const v = args[key];
|
|
572
602
|
if (v !== undefined && v !== null && v !== '')
|
|
573
603
|
bits.push(`${key.replace('_', ' ')}: ${String(v)}`);
|