@yagni-app/code-staging 1.1.6-staging.1443.1 → 1.1.6-staging.1445.1
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.
|
@@ -362,6 +362,9 @@ function formatSummaryForChat(result, costText) {
|
|
|
362
362
|
const UNTRACKED_NOTICE = {
|
|
363
363
|
disabled: "Run tracking is off for this workspace, so this /go run won't appear on the Work page.",
|
|
364
364
|
unauthorized: "Run tracking is unauthorized. Run `yagni login` to refresh your token so runs appear on the Work page.",
|
|
365
|
+
// A refusal always arrives with the server's own sentence; this is the line
|
|
366
|
+
// for the rare body that carries none.
|
|
367
|
+
refused: "YAGNI declined to record this run, so it won't appear on the Work page. The pipeline ran anyway.",
|
|
365
368
|
error: "Couldn't reach YAGNI to record this run, so it won't appear on the Work page. The pipeline ran anyway.",
|
|
366
369
|
};
|
|
367
370
|
/** Toast severity for an outcome: clean is good, round_cap warns, abort/fail are errors. */
|
|
@@ -737,9 +740,11 @@ export function registerGoCommand(pi, deps = {}) {
|
|
|
737
740
|
}
|
|
738
741
|
return { runId: started.runId, workItemId: started.workItemId };
|
|
739
742
|
}
|
|
740
|
-
// Recording skipped (flag off) or failed (unauthorized /
|
|
741
|
-
// tell the user the run won't show on the Work page
|
|
742
|
-
|
|
743
|
+
// Recording skipped (flag off) or failed (unauthorized / refused /
|
|
744
|
+
// unreachable): tell the user the run won't show on the Work page
|
|
745
|
+
// rather than swallowing it. A refusal carries the server's own
|
|
746
|
+
// sentence, which says what to do about it, so it wins over our copy.
|
|
747
|
+
notify(started.message ?? UNTRACKED_NOTICE[started.reason], "warning");
|
|
743
748
|
return {};
|
|
744
749
|
};
|
|
745
750
|
let resumeFrom;
|
|
@@ -67,21 +67,27 @@ export interface RunStartResult extends RunSessionHandle {
|
|
|
67
67
|
* workspace (the write endpoints 404 to hide themselves).
|
|
68
68
|
* - `unauthorized` — 401/403: the device token is missing/expired or lacks the
|
|
69
69
|
* `yagni_code:write` scope (re-run `yagni login`).
|
|
70
|
+
* - `refused` — 400: the server declined for a reason a person can act on
|
|
71
|
+
* and said so in one sentence (this repository belongs to
|
|
72
|
+
* no Team). The sentence rides `message` and is what the
|
|
73
|
+
* caller shows.
|
|
70
74
|
* - `error` — anything else: an over-budget 402, a 5xx, a 2xx with an
|
|
71
75
|
* unrecognized body, or an unreachable backend / thrown
|
|
72
76
|
* network error.
|
|
73
77
|
*/
|
|
74
|
-
export type RunTrackingFailure = "disabled" | "unauthorized" | "error";
|
|
78
|
+
export type RunTrackingFailure = "disabled" | "unauthorized" | "refused" | "error";
|
|
75
79
|
/**
|
|
76
80
|
* The outcome of opening a run. `tracked: true` carries the handle + gate
|
|
77
81
|
* verdict; `tracked: false` carries the reason so `/go` can warn that this run
|
|
78
|
-
* won't appear on the Work page
|
|
82
|
+
* won't appear on the Work page, plus the server's own sentence when it sent
|
|
83
|
+
* one. `start` never returns null and never throws.
|
|
79
84
|
*/
|
|
80
85
|
export type RunStartOutcome = ({
|
|
81
86
|
tracked: true;
|
|
82
87
|
} & RunStartResult) | {
|
|
83
88
|
tracked: false;
|
|
84
89
|
reason: RunTrackingFailure;
|
|
90
|
+
message?: string;
|
|
85
91
|
};
|
|
86
92
|
/** Arguments to open a run. */
|
|
87
93
|
export interface StartArgs {
|
|
@@ -25,27 +25,45 @@ function failureReason(status) {
|
|
|
25
25
|
return "disabled";
|
|
26
26
|
if (status === 401 || status === 403)
|
|
27
27
|
return "unauthorized";
|
|
28
|
+
if (status === 400)
|
|
29
|
+
return "refused";
|
|
28
30
|
return "error";
|
|
29
31
|
}
|
|
30
32
|
/** Cap a debug body excerpt so a giant HTML/JSON error never floods the channel. */
|
|
31
33
|
const MAX_DEBUG_BODY = 200;
|
|
34
|
+
/** Cap the sentence a refusal is allowed to print, so a stray body stays one line. */
|
|
35
|
+
const MAX_REFUSAL_MESSAGE = 240;
|
|
32
36
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* so
|
|
36
|
-
*
|
|
37
|
+
* The server's one-sentence refusal, if the body is the shape our own routes
|
|
38
|
+
* send (`{ error }`). Anything else, including HTML from a proxy, reads as no
|
|
39
|
+
* sentence at all, so the caller falls back to its own copy. Newlines are
|
|
40
|
+
* folded so a notice is never a stack.
|
|
37
41
|
*/
|
|
38
|
-
|
|
42
|
+
function refusalMessage(body) {
|
|
39
43
|
try {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
const parsed = JSON.parse(body);
|
|
45
|
+
const text = typeof parsed.error === "string" ? parsed.error.trim() : "";
|
|
46
|
+
if (!text)
|
|
47
|
+
return undefined;
|
|
48
|
+
const oneLine = text.replace(/\s+/g, " ");
|
|
49
|
+
return oneLine.length > MAX_REFUSAL_MESSAGE
|
|
50
|
+
? `${oneLine.slice(0, MAX_REFUSAL_MESSAGE)}...`
|
|
51
|
+
: oneLine;
|
|
44
52
|
}
|
|
45
53
|
catch {
|
|
46
|
-
return
|
|
54
|
+
return undefined;
|
|
47
55
|
}
|
|
48
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* A token-free, capped `: <body>` suffix for a failed-response debug line, from
|
|
59
|
+
* a body already read once by the `!res.ok` branch.
|
|
60
|
+
*/
|
|
61
|
+
function bodyTail(body) {
|
|
62
|
+
const t = body.trim();
|
|
63
|
+
if (!t)
|
|
64
|
+
return "";
|
|
65
|
+
return `: ${t.length > MAX_DEBUG_BODY ? `${t.slice(0, MAX_DEBUG_BODY)} ...` : t}`;
|
|
66
|
+
}
|
|
49
67
|
export function makeRunSession(opts) {
|
|
50
68
|
const f = opts.fetchImpl ?? fetch;
|
|
51
69
|
let handle = null;
|
|
@@ -94,9 +112,21 @@ export function makeRunSession(opts) {
|
|
|
94
112
|
...(opts.random ? { random: opts.random } : {}),
|
|
95
113
|
});
|
|
96
114
|
if (!res.ok) {
|
|
115
|
+
// Read the body ONCE on the failure branch (the ok path reads
|
|
116
|
+
// `res.json()` instead, so there is no double-consume): the debug line
|
|
117
|
+
// and the refusal sentence both come from this one read, and a body
|
|
118
|
+
// that cannot be read costs neither.
|
|
119
|
+
let body = "";
|
|
120
|
+
try {
|
|
121
|
+
body = await res.text();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
/* a body we cannot read is a body we do not quote */
|
|
125
|
+
}
|
|
97
126
|
if (debugOn)
|
|
98
|
-
debug(`[yagni-code] run-record ${path} -> HTTP ${res.status}${
|
|
99
|
-
|
|
127
|
+
debug(`[yagni-code] run-record ${path} -> HTTP ${res.status}${bodyTail(body)}`);
|
|
128
|
+
const message = refusalMessage(body);
|
|
129
|
+
return { ok: false, status: res.status, ...(message ? { failureText: message } : {}) };
|
|
100
130
|
}
|
|
101
131
|
return { ok: true, data: (await res.json()) };
|
|
102
132
|
}
|
|
@@ -119,8 +149,13 @@ export function makeRunSession(opts) {
|
|
|
119
149
|
// deduped by the server (startCodeRun.findRunByIdempotencyKey) instead of
|
|
120
150
|
// opening a second run + a second free-text work item.
|
|
121
151
|
const r = await post("runs", { ...b, idempotencyKey: randomUUID() }, { retry: true });
|
|
122
|
-
if (!r.ok)
|
|
123
|
-
return {
|
|
152
|
+
if (!r.ok) {
|
|
153
|
+
return {
|
|
154
|
+
tracked: false,
|
|
155
|
+
reason: failureReason(r.status),
|
|
156
|
+
...(r.failureText ? { message: r.failureText } : {}),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
124
159
|
const data = r.data;
|
|
125
160
|
if (typeof data.runId === "string" && typeof data.workItemId === "string") {
|
|
126
161
|
handle = { runId: data.runId, workItemId: data.workItemId };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.1.6-staging.
|
|
3
|
+
"version": "1.1.6-staging.1445.1",
|
|
4
4
|
"description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"turndown": "^7.2.4",
|
|
59
59
|
"typebox": "^1.3.15"
|
|
60
60
|
},
|
|
61
|
-
"yagniSourceSha": "
|
|
61
|
+
"yagniSourceSha": "df1d49933fa6a71d3b18754a183b5e400a45053a"
|
|
62
62
|
}
|