@lazyingart/agintiflow 0.2.0 → 0.4.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 +32 -2
- package/docs/project-agent-lessons.md +41 -0
- package/package.json +1 -1
- package/public/app.js +225 -11
- package/public/index.html +41 -0
- package/public/styles.css +115 -5
- package/scripts/smoke-coding-tools.js +19 -4
- package/scripts/smoke-web-api.js +43 -0
- package/src/agent-runner.js +14 -0
- package/src/cli.js +179 -0
- package/src/config.js +6 -2
- package/src/model-client.js +28 -7
- package/src/project.js +332 -0
- package/src/task-profiles.js +85 -0
- package/src/web-db.js +6 -2
- package/web.js +47 -6
package/public/styles.css
CHANGED
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
box-sizing: border-box;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
html,
|
|
18
|
+
body {
|
|
19
|
+
width: 100%;
|
|
20
|
+
max-width: 100%;
|
|
21
|
+
overflow-x: hidden;
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
body {
|
|
18
25
|
margin: 0;
|
|
19
26
|
font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
|
|
@@ -25,20 +32,24 @@ body {
|
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
.page {
|
|
28
|
-
width: min(1240px,
|
|
35
|
+
width: min(1240px, 100%);
|
|
36
|
+
max-width: 100%;
|
|
29
37
|
margin: 32px auto;
|
|
38
|
+
padding: 0 16px;
|
|
30
39
|
display: grid;
|
|
31
40
|
gap: 20px;
|
|
32
|
-
grid-template-columns: minmax(
|
|
41
|
+
grid-template-columns: minmax(280px, 400px) minmax(0, 1fr);
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
.stack {
|
|
36
45
|
display: grid;
|
|
46
|
+
min-width: 0;
|
|
37
47
|
align-content: start;
|
|
38
48
|
gap: 14px;
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
.panel {
|
|
52
|
+
min-width: 0;
|
|
42
53
|
background: var(--panel);
|
|
43
54
|
border: 1px solid var(--line);
|
|
44
55
|
border-radius: 18px;
|
|
@@ -109,6 +120,27 @@ h1 {
|
|
|
109
120
|
line-height: 1.45;
|
|
110
121
|
}
|
|
111
122
|
|
|
123
|
+
.project-card,
|
|
124
|
+
.setup-card {
|
|
125
|
+
display: grid;
|
|
126
|
+
min-width: 0;
|
|
127
|
+
gap: 10px;
|
|
128
|
+
margin: 14px 0;
|
|
129
|
+
padding: 13px;
|
|
130
|
+
border: 1px solid rgba(15, 118, 110, 0.18);
|
|
131
|
+
border-radius: 16px;
|
|
132
|
+
background: linear-gradient(135deg, rgba(204, 251, 241, 0.44), rgba(255, 247, 237, 0.9));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.project-card p,
|
|
136
|
+
.setup-card p {
|
|
137
|
+
overflow-wrap: anywhere;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.setup-card[hidden] {
|
|
141
|
+
display: none;
|
|
142
|
+
}
|
|
143
|
+
|
|
112
144
|
form,
|
|
113
145
|
.checks {
|
|
114
146
|
display: grid;
|
|
@@ -137,6 +169,7 @@ button {
|
|
|
137
169
|
input,
|
|
138
170
|
textarea,
|
|
139
171
|
select {
|
|
172
|
+
min-width: 0;
|
|
140
173
|
width: 100%;
|
|
141
174
|
border: 1px solid var(--line);
|
|
142
175
|
background: #fffdfa;
|
|
@@ -306,6 +339,8 @@ button.danger {
|
|
|
306
339
|
.actions,
|
|
307
340
|
.header-row {
|
|
308
341
|
display: flex;
|
|
342
|
+
min-width: 0;
|
|
343
|
+
flex-wrap: wrap;
|
|
309
344
|
gap: 12px;
|
|
310
345
|
align-items: center;
|
|
311
346
|
justify-content: space-between;
|
|
@@ -417,6 +452,7 @@ button.danger {
|
|
|
417
452
|
font-size: 0.72rem;
|
|
418
453
|
line-height: 1.42;
|
|
419
454
|
white-space: pre-wrap;
|
|
455
|
+
overflow-wrap: anywhere;
|
|
420
456
|
}
|
|
421
457
|
|
|
422
458
|
.sandbox-card h2 {
|
|
@@ -444,6 +480,7 @@ button.danger {
|
|
|
444
480
|
font-size: 0.78rem;
|
|
445
481
|
line-height: 1.45;
|
|
446
482
|
white-space: pre-wrap;
|
|
483
|
+
overflow-wrap: anywhere;
|
|
447
484
|
}
|
|
448
485
|
|
|
449
486
|
.session-select {
|
|
@@ -452,13 +489,18 @@ button.danger {
|
|
|
452
489
|
|
|
453
490
|
.session-tools {
|
|
454
491
|
display: flex;
|
|
455
|
-
|
|
492
|
+
flex: 1 1 420px;
|
|
493
|
+
min-width: 0;
|
|
494
|
+
max-width: 100%;
|
|
495
|
+
flex-wrap: wrap;
|
|
456
496
|
gap: 8px;
|
|
457
497
|
align-items: center;
|
|
458
498
|
justify-content: flex-end;
|
|
459
499
|
}
|
|
460
500
|
|
|
461
501
|
.session-tools .session-select {
|
|
502
|
+
flex: 1 1 220px;
|
|
503
|
+
min-width: 0;
|
|
462
504
|
max-width: none;
|
|
463
505
|
}
|
|
464
506
|
|
|
@@ -476,6 +518,7 @@ button.danger {
|
|
|
476
518
|
font-size: 0.9rem;
|
|
477
519
|
line-height: 1.5;
|
|
478
520
|
white-space: pre-wrap;
|
|
521
|
+
overflow-wrap: anywhere;
|
|
479
522
|
}
|
|
480
523
|
|
|
481
524
|
.chat-panel {
|
|
@@ -514,6 +557,7 @@ button.danger {
|
|
|
514
557
|
|
|
515
558
|
.chat-content {
|
|
516
559
|
line-height: 1.5;
|
|
560
|
+
overflow-wrap: anywhere;
|
|
517
561
|
}
|
|
518
562
|
|
|
519
563
|
.chat-form {
|
|
@@ -522,6 +566,7 @@ button.danger {
|
|
|
522
566
|
|
|
523
567
|
.modal {
|
|
524
568
|
width: min(720px, calc(100% - 28px));
|
|
569
|
+
max-width: 100%;
|
|
525
570
|
padding: 0;
|
|
526
571
|
border: 0;
|
|
527
572
|
border-radius: 22px;
|
|
@@ -604,6 +649,7 @@ button.danger {
|
|
|
604
649
|
|
|
605
650
|
.artifact-modal {
|
|
606
651
|
width: min(1120px, calc(100% - 28px));
|
|
652
|
+
max-width: 100%;
|
|
607
653
|
}
|
|
608
654
|
|
|
609
655
|
.artifact-modal-card {
|
|
@@ -613,6 +659,7 @@ button.danger {
|
|
|
613
659
|
|
|
614
660
|
.artifact-tabs {
|
|
615
661
|
display: flex;
|
|
662
|
+
max-width: 100%;
|
|
616
663
|
flex-wrap: wrap;
|
|
617
664
|
gap: 8px;
|
|
618
665
|
padding: 5px;
|
|
@@ -623,7 +670,7 @@ button.danger {
|
|
|
623
670
|
|
|
624
671
|
.artifact-tab {
|
|
625
672
|
flex: 1;
|
|
626
|
-
min-width: 130px;
|
|
673
|
+
min-width: min(130px, 100%);
|
|
627
674
|
border: 1px solid transparent;
|
|
628
675
|
background: transparent;
|
|
629
676
|
color: var(--muted);
|
|
@@ -639,6 +686,7 @@ button.danger {
|
|
|
639
686
|
.artifact-layout {
|
|
640
687
|
display: grid;
|
|
641
688
|
min-height: 520px;
|
|
689
|
+
max-width: 100%;
|
|
642
690
|
gap: 14px;
|
|
643
691
|
grid-template-columns: minmax(240px, 340px) minmax(0, 1fr);
|
|
644
692
|
overflow: hidden;
|
|
@@ -674,6 +722,7 @@ button.danger {
|
|
|
674
722
|
.artifact-list {
|
|
675
723
|
display: grid;
|
|
676
724
|
align-content: start;
|
|
725
|
+
min-width: 0;
|
|
677
726
|
gap: 8px;
|
|
678
727
|
overflow: auto;
|
|
679
728
|
padding: 10px;
|
|
@@ -681,7 +730,8 @@ button.danger {
|
|
|
681
730
|
|
|
682
731
|
.artifact-row {
|
|
683
732
|
display: grid;
|
|
684
|
-
|
|
733
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
734
|
+
gap: 8px;
|
|
685
735
|
width: 100%;
|
|
686
736
|
padding: 11px 12px;
|
|
687
737
|
border: 1px solid var(--line);
|
|
@@ -691,6 +741,40 @@ button.danger {
|
|
|
691
741
|
text-align: left;
|
|
692
742
|
}
|
|
693
743
|
|
|
744
|
+
.artifact-row-main {
|
|
745
|
+
display: grid;
|
|
746
|
+
min-width: 0;
|
|
747
|
+
gap: 5px;
|
|
748
|
+
padding: 0;
|
|
749
|
+
border: 0;
|
|
750
|
+
border-radius: 0;
|
|
751
|
+
background: transparent;
|
|
752
|
+
color: inherit;
|
|
753
|
+
box-shadow: none;
|
|
754
|
+
text-align: left;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.artifact-menu-button {
|
|
758
|
+
width: 34px;
|
|
759
|
+
min-width: 34px;
|
|
760
|
+
height: 34px;
|
|
761
|
+
align-self: start;
|
|
762
|
+
padding: 0;
|
|
763
|
+
border: 1px solid var(--line);
|
|
764
|
+
border-radius: 999px;
|
|
765
|
+
background: rgba(255, 253, 250, 0.9);
|
|
766
|
+
color: var(--muted);
|
|
767
|
+
font-weight: 900;
|
|
768
|
+
letter-spacing: 0.08em;
|
|
769
|
+
box-shadow: none;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
.artifact-menu-button:hover,
|
|
773
|
+
.artifact-menu-button:focus-visible {
|
|
774
|
+
border-color: rgba(15, 118, 110, 0.34);
|
|
775
|
+
color: var(--accent);
|
|
776
|
+
}
|
|
777
|
+
|
|
694
778
|
.artifact-row[data-selected="true"] {
|
|
695
779
|
border-color: rgba(15, 118, 110, 0.62);
|
|
696
780
|
box-shadow: 0 0 0 2px rgba(15, 118, 110, 0.1);
|
|
@@ -715,6 +799,7 @@ button.danger {
|
|
|
715
799
|
.artifact-row-top,
|
|
716
800
|
.artifact-viewer-header {
|
|
717
801
|
display: flex;
|
|
802
|
+
min-width: 0;
|
|
718
803
|
gap: 10px;
|
|
719
804
|
align-items: flex-start;
|
|
720
805
|
justify-content: space-between;
|
|
@@ -722,6 +807,7 @@ button.danger {
|
|
|
722
807
|
|
|
723
808
|
.artifact-row-top strong,
|
|
724
809
|
.artifact-viewer-header h3 {
|
|
810
|
+
min-width: 0;
|
|
725
811
|
overflow: hidden;
|
|
726
812
|
text-overflow: ellipsis;
|
|
727
813
|
}
|
|
@@ -752,6 +838,7 @@ button.danger {
|
|
|
752
838
|
.artifact-row-preview {
|
|
753
839
|
display: -webkit-box;
|
|
754
840
|
overflow: hidden;
|
|
841
|
+
overflow-wrap: anywhere;
|
|
755
842
|
-webkit-box-orient: vertical;
|
|
756
843
|
-webkit-line-clamp: 2;
|
|
757
844
|
}
|
|
@@ -808,6 +895,7 @@ button.danger {
|
|
|
808
895
|
|
|
809
896
|
.artifact-pdf-frame {
|
|
810
897
|
width: 100%;
|
|
898
|
+
max-width: 100%;
|
|
811
899
|
min-height: 62vh;
|
|
812
900
|
border: 1px solid var(--line);
|
|
813
901
|
border-radius: 14px;
|
|
@@ -828,6 +916,8 @@ button.danger {
|
|
|
828
916
|
|
|
829
917
|
@media (max-width: 960px) {
|
|
830
918
|
.page {
|
|
919
|
+
margin: 16px auto;
|
|
920
|
+
padding: 0 12px;
|
|
831
921
|
grid-template-columns: 1fr;
|
|
832
922
|
}
|
|
833
923
|
|
|
@@ -876,3 +966,23 @@ button.danger {
|
|
|
876
966
|
overflow: auto;
|
|
877
967
|
}
|
|
878
968
|
}
|
|
969
|
+
|
|
970
|
+
@media (max-width: 720px) {
|
|
971
|
+
.grid,
|
|
972
|
+
.checks,
|
|
973
|
+
.wrapper-controls {
|
|
974
|
+
grid-template-columns: 1fr;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.panel {
|
|
978
|
+
padding: 15px;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.artifact-tabs {
|
|
982
|
+
border-radius: 18px;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.artifact-tab {
|
|
986
|
+
min-width: 120px;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
@@ -17,7 +17,7 @@ function assert(condition, message) {
|
|
|
17
17
|
if (!condition) throw new Error(message);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async function runMock(goal, sessionId) {
|
|
20
|
+
async function runMock(goal, sessionId, { resume = false } = {}) {
|
|
21
21
|
const config = resolveRuntimeConfig(
|
|
22
22
|
{
|
|
23
23
|
provider: "mock",
|
|
@@ -26,6 +26,7 @@ async function runMock(goal, sessionId) {
|
|
|
26
26
|
goal,
|
|
27
27
|
commandCwd: workspace,
|
|
28
28
|
maxSteps: 5,
|
|
29
|
+
resume: resume ? sessionId : "",
|
|
29
30
|
},
|
|
30
31
|
{
|
|
31
32
|
baseDir: runtimeDir,
|
|
@@ -38,7 +39,7 @@ async function runMock(goal, sessionId) {
|
|
|
38
39
|
allowFileTools: true,
|
|
39
40
|
sandboxMode: "host",
|
|
40
41
|
packageInstallPolicy: "block",
|
|
41
|
-
sessionId,
|
|
42
|
+
sessionId: resume ? "" : sessionId,
|
|
42
43
|
}
|
|
43
44
|
);
|
|
44
45
|
|
|
@@ -78,11 +79,23 @@ try {
|
|
|
78
79
|
"repaired DeepSeek history retained an orphan stale tool message"
|
|
79
80
|
);
|
|
80
81
|
|
|
81
|
-
const writeRun = await runMock("Create
|
|
82
|
-
const written = await fs.readFile(path.join(workspace, "notes/
|
|
82
|
+
const writeRun = await runMock("Create notes/hello.md with a short coding smoke message.", "coding-write");
|
|
83
|
+
const written = await fs.readFile(path.join(workspace, "notes/hello.md"), "utf8");
|
|
83
84
|
assert(written.includes("Created by AgInTiFlow mock mode."), "mock write did not create expected file");
|
|
84
85
|
assert(writeRun.events.some((event) => event.type === "file.changed"), "write run did not persist file.changed event");
|
|
85
86
|
|
|
87
|
+
await runMock("Create notes/resume.md with resumed session content.", "coding-write", { resume: true });
|
|
88
|
+
const resumed = await fs.readFile(path.join(workspace, "notes/resume.md"), "utf8");
|
|
89
|
+
assert(resumed.includes("Created by AgInTiFlow mock mode."), "mock resume did not create a new requested file");
|
|
90
|
+
|
|
91
|
+
let duplicateFailed = false;
|
|
92
|
+
try {
|
|
93
|
+
await runMock("Create notes/hello.md with duplicate content.", "coding-write-duplicate");
|
|
94
|
+
} catch (error) {
|
|
95
|
+
duplicateFailed = /File already exists|Mock tool failed/.test(String(error));
|
|
96
|
+
}
|
|
97
|
+
assert(duplicateFailed, "duplicate mock write did not fail safely");
|
|
98
|
+
|
|
86
99
|
await runMock("Create file: /workspace/virtual-output.txt with virtual Docker path support.", "coding-write-virtual");
|
|
87
100
|
const virtualWritten = await fs.readFile(path.join(workspace, "virtual-output.txt"), "utf8");
|
|
88
101
|
assert(virtualWritten.includes("Created by AgInTiFlow mock mode."), "virtual /workspace path was not mapped safely");
|
|
@@ -123,6 +136,8 @@ try {
|
|
|
123
136
|
checks: [
|
|
124
137
|
"deepseek_history_repair",
|
|
125
138
|
"write_file",
|
|
139
|
+
"duplicate_write_failed",
|
|
140
|
+
"resume_session_write",
|
|
126
141
|
"virtual_workspace_path",
|
|
127
142
|
"apply_patch",
|
|
128
143
|
"block_env",
|
package/scripts/smoke-web-api.js
CHANGED
|
@@ -74,6 +74,23 @@ try {
|
|
|
74
74
|
if (!config.keyStatus?.mock) throw new Error("mock provider is not advertised by /api/config");
|
|
75
75
|
if (!config.workspace?.enabled) throw new Error("workspace file tools are not advertised by /api/config");
|
|
76
76
|
if (config.preferences?.preferredWrapper !== "codex") throw new Error("Codex is not the default preferred wrapper");
|
|
77
|
+
if (config.project?.root !== runtimeDir) throw new Error("web project root did not default to launch directory");
|
|
78
|
+
if (config.preferences?.commandCwd !== runtimeDir) throw new Error("commandCwd did not default to project root");
|
|
79
|
+
if (!Array.isArray(config.taskProfiles) || !config.taskProfiles.some((profile) => profile.id === "latex")) {
|
|
80
|
+
throw new Error("task profiles are not advertised by /api/config");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const keyStatus = await fetchJson("/api/keys/status");
|
|
84
|
+
if (typeof keyStatus.keyStatus?.deepseek !== "boolean") throw new Error("key status endpoint is invalid");
|
|
85
|
+
if ("localEnvPath" in keyStatus.keyStatus) throw new Error("key status leaked a local env path");
|
|
86
|
+
const savedKey = await fetchJson("/api/keys/deepseek", {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/json" },
|
|
89
|
+
body: JSON.stringify({ apiKey: "test-deepseek-key-not-real" }),
|
|
90
|
+
});
|
|
91
|
+
if (!savedKey.ok || !savedKey.keyStatus?.deepseek || "apiKey" in savedKey || "key" in savedKey) {
|
|
92
|
+
throw new Error("local key save endpoint returned invalid or sensitive data");
|
|
93
|
+
}
|
|
77
94
|
|
|
78
95
|
const status = await fetchJson("/api/sandbox/status");
|
|
79
96
|
if (!status.status?.workspaceReadable) throw new Error("sandbox status did not report a readable workspace");
|
|
@@ -111,6 +128,30 @@ try {
|
|
|
111
128
|
if (run.status !== "finished") throw new Error(`mock run failed: ${run.error || "unknown error"}`);
|
|
112
129
|
if (!/Mock run complete/.test(run.result)) throw new Error("mock run did not return the expected result");
|
|
113
130
|
|
|
131
|
+
const fileRunStart = await fetchJson("/api/runs", {
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers: { "Content-Type": "application/json" },
|
|
134
|
+
body: JSON.stringify({
|
|
135
|
+
provider: "mock",
|
|
136
|
+
routingMode: "manual",
|
|
137
|
+
model: "mock-agent",
|
|
138
|
+
goal: "Create notes/hello.md with safe web API content.",
|
|
139
|
+
commandCwd: runtimeDir,
|
|
140
|
+
sandboxMode: "host",
|
|
141
|
+
packageInstallPolicy: "block",
|
|
142
|
+
allowShellTool: false,
|
|
143
|
+
allowFileTools: true,
|
|
144
|
+
preferredWrapper: "codex",
|
|
145
|
+
maxSteps: 4,
|
|
146
|
+
headless: true,
|
|
147
|
+
taskProfile: "code",
|
|
148
|
+
}),
|
|
149
|
+
});
|
|
150
|
+
const fileRun = await waitForRun(fileRunStart.sessionId);
|
|
151
|
+
if (fileRun.status !== "finished") throw new Error(`mock file run failed: ${fileRun.error || "unknown error"}`);
|
|
152
|
+
const hello = await fs.readFile(path.join(runtimeDir, "notes", "hello.md"), "utf8");
|
|
153
|
+
if (!hello.includes("Created by AgInTiFlow mock mode.")) throw new Error("mock file run did not create requested path");
|
|
154
|
+
|
|
114
155
|
const chat = await fetchJson(`/api/sessions/${encodeURIComponent(runStart.sessionId)}/chat`);
|
|
115
156
|
if (!Array.isArray(chat.chat) || chat.chat.length < 2) throw new Error("chat history was not persisted");
|
|
116
157
|
|
|
@@ -189,6 +230,8 @@ try {
|
|
|
189
230
|
ok: true,
|
|
190
231
|
endpoints: [
|
|
191
232
|
"/api/config",
|
|
233
|
+
"/api/keys/status",
|
|
234
|
+
"POST /api/keys/:provider",
|
|
192
235
|
"/api/sandbox/status",
|
|
193
236
|
"/api/sandbox/preflight",
|
|
194
237
|
"/api/runs",
|
package/src/agent-runner.js
CHANGED
|
@@ -13,6 +13,7 @@ import { evaluateCommandPolicy } from "./command-policy.js";
|
|
|
13
13
|
import { redactSensitiveText, redactValue } from "./redaction.js";
|
|
14
14
|
import { executeWorkspaceTool, summarizeWorkspaceTools, WORKSPACE_TOOL_NAMES } from "./workspace-tools.js";
|
|
15
15
|
import { normalizeCanvasPayload } from "./artifact-tunnel.js";
|
|
16
|
+
import { getTaskProfile } from "./task-profiles.js";
|
|
16
17
|
|
|
17
18
|
const exec = promisify(execCallback);
|
|
18
19
|
const BROWSER_TOOLS = new Set(["open_url", "click", "type", "scroll", "press", "back"]);
|
|
@@ -108,6 +109,7 @@ export function repairModelMessageHistory(state, config = {}) {
|
|
|
108
109
|
|
|
109
110
|
function createInitialState(config, sessionId) {
|
|
110
111
|
const now = new Date().toISOString();
|
|
112
|
+
const taskProfile = getTaskProfile(config.taskProfile);
|
|
111
113
|
return {
|
|
112
114
|
sessionId,
|
|
113
115
|
createdAt: now,
|
|
@@ -151,6 +153,7 @@ function createInitialState(config, sessionId) {
|
|
|
151
153
|
config.allowWrapperTools
|
|
152
154
|
? `External coding-agent wrappers are available as advisory tools only. Use the selected wrapper only: ${normalizeWrapperName(config.preferredWrapper)}. Wrapper status: ${wrapperStatusText()}.`
|
|
153
155
|
: "External coding-agent wrappers are disabled.",
|
|
156
|
+
`Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
|
|
154
157
|
"A frontend canvas/artifacts tunnel exists. Use send_to_canvas when important markdown, diffs, screenshots, images, or workspace files should be highlighted in the UI. It is optional and ordinary final text can still go directly to finish.",
|
|
155
158
|
"For visual-output requests such as draw, plot, graph, chart, diagram, figure, image, or visualization, proactively publish a canvas artifact even when the user does not mention canvas. If workspace file tools are enabled, prefer creating a small SVG or markdown artifact and call send_to_canvas with selected=true.",
|
|
156
159
|
"For LaTeX/PDF requests, create the needed source/assets, compile with the available allowlisted TeX toolchain, and publish the resulting PDF through send_to_canvas. For subfolder documents, keep outputs beside the source. Use pdflatex-compatible figure formats such as PDF or PNG.",
|
|
@@ -172,6 +175,7 @@ function createInitialState(config, sessionId) {
|
|
|
172
175
|
config.allowWrapperTools
|
|
173
176
|
? `Agent wrappers: selected=${normalizeWrapperName(config.preferredWrapper)}; ${wrapperStatusText()}`
|
|
174
177
|
: "",
|
|
178
|
+
`Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
|
|
175
179
|
"Canvas/artifacts tunnel: available through send_to_canvas for optional frontend rendering.",
|
|
176
180
|
"Visual-output requests should produce a canvas artifact without requiring the user to ask for canvas explicitly.",
|
|
177
181
|
"LaTeX/PDF requests should produce source artifacts and, when possible, a compiled PDF artifact. For subfolder documents, keep outputs beside the source. For figure-in-document tasks, create PDF/PNG figures that pdflatex can include.",
|
|
@@ -248,6 +252,7 @@ function appendChatEntry(state, role, content) {
|
|
|
248
252
|
function applyContinuationPrompt(state, config, observers) {
|
|
249
253
|
if (!config.resume || !config.goal) return;
|
|
250
254
|
|
|
255
|
+
const taskProfile = getTaskProfile(config.taskProfile);
|
|
251
256
|
ensureChatState(state);
|
|
252
257
|
state.goal = config.goal;
|
|
253
258
|
state.provider = config.provider;
|
|
@@ -271,6 +276,7 @@ function applyContinuationPrompt(state, config, observers) {
|
|
|
271
276
|
config.allowWrapperTools
|
|
272
277
|
? `Agent wrappers: selected=${normalizeWrapperName(config.preferredWrapper)}; ${wrapperStatusText()}`
|
|
273
278
|
: "",
|
|
279
|
+
`Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
|
|
274
280
|
]
|
|
275
281
|
.filter(Boolean)
|
|
276
282
|
.join("\n"),
|
|
@@ -754,6 +760,7 @@ export async function runAgent(config) {
|
|
|
754
760
|
model: config.model,
|
|
755
761
|
routingMode: config.routingMode,
|
|
756
762
|
routeReason: config.routeReason,
|
|
763
|
+
taskProfile: config.taskProfile,
|
|
757
764
|
commandCwd: config.commandCwd,
|
|
758
765
|
allowShellTool: config.allowShellTool,
|
|
759
766
|
allowWrapperTools: config.allowWrapperTools,
|
|
@@ -817,6 +824,7 @@ export async function runAgent(config) {
|
|
|
817
824
|
plan: state.plan || "",
|
|
818
825
|
suggestedStartUrl: config.startUrl || "",
|
|
819
826
|
canvasArtifactsAvailable: true,
|
|
827
|
+
taskProfile: getTaskProfile(config.taskProfile),
|
|
820
828
|
})}`,
|
|
821
829
|
});
|
|
822
830
|
|
|
@@ -899,6 +907,12 @@ export async function runAgent(config) {
|
|
|
899
907
|
});
|
|
900
908
|
}
|
|
901
909
|
|
|
910
|
+
if (config.provider === "mock" && toolResult.ok === false && !toolResult.blocked) {
|
|
911
|
+
throw new Error(
|
|
912
|
+
`Mock tool failed: ${toolResult.error || toolResult.reason || `${toolResult.toolName || "tool"} returned ok=false`}`
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
|
|
902
916
|
if (toolResult.done) {
|
|
903
917
|
state.stepsCompleted = step;
|
|
904
918
|
state.updatedAt = new Date().toISOString();
|