@yagni-app/code-staging 0.2.1-staging.1034.1 → 0.2.1-staging.1038.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.
|
@@ -97,6 +97,7 @@ export declare function parseSpendResponse(data: unknown): SpendResponse | null;
|
|
|
97
97
|
export declare function registerYagni(pi: ExtensionAPI, deps?: RegisterYagniDeps): Promise<void>;
|
|
98
98
|
export default function (pi: ExtensionAPI): Promise<void>;
|
|
99
99
|
export { makeAskYagniTool } from "./askYagniTool.js";
|
|
100
|
+
export { makeFileTicketTool, makeUpdateTicketStatusTool } from "./ticketTools.js";
|
|
100
101
|
export { makeAskAdvisorTool, registerAdviseCommand } from "./askAdvisorTool.js";
|
|
101
102
|
export { ADVISOR_TIER, DEFAULT_ADVISOR_LIMITS, decideConsult, formatAdvisorSubtotal, makeAdvisorState, } from "./advisor.js";
|
|
102
103
|
export type { Citation, MakeAskYagniToolOptions } from "./askYagniTool.js";
|
package/dist/extension/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Text } from "@earendil-works/pi-tui";
|
|
|
4
4
|
import { DEFAULT_ADVISOR_LIMITS, formatAdvisorSubtotal, makeAdvisorState } from "./advisor.js";
|
|
5
5
|
import { makeAskAdvisorTool, registerAdviseCommand } from "./askAdvisorTool.js";
|
|
6
6
|
import { makeAskYagniTool } from "./askYagniTool.js";
|
|
7
|
+
import { makeFileTicketTool, makeUpdateTicketStatusTool } from "./ticketTools.js";
|
|
7
8
|
import { makeReviewBusinessMatchTool } from "./reviewTool.js";
|
|
8
9
|
import { registerCmuxBridge } from "./cmux/index.js";
|
|
9
10
|
import { makeRecordEngineeringContextTool } from "./recordContextTool.js";
|
|
@@ -126,6 +127,12 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
126
127
|
pi.registerProvider("yagni", buildYagniProvider(catalog, baseUrl, attributionHeaders(deps.env)));
|
|
127
128
|
const toolOpts = { baseUrl, getToken: getTokenFn, fetchImpl: authedFetch };
|
|
128
129
|
pi.registerTool(makeAskYagniTool(toolOpts));
|
|
130
|
+
// Ticket write-back (spec 2026-08-09): explicit user-intent writes to the
|
|
131
|
+
// workspace tracker, attributed to the developer via per-user credentials.
|
|
132
|
+
if (!evalMode) {
|
|
133
|
+
pi.registerTool(makeFileTicketTool(toolOpts));
|
|
134
|
+
pi.registerTool(makeUpdateTicketStatusTool(toolOpts));
|
|
135
|
+
}
|
|
129
136
|
// The peak-tier escalation for Advanced sessions (YAG-380). Registered
|
|
130
137
|
// UNCONDITIONALLY and gated at execute time on the live session model: pi's
|
|
131
138
|
// picker can switch the model after activation, so a registration-time tier
|
|
@@ -225,6 +232,7 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
225
232
|
...DEFAULT_PERMISSION_POLICY.reviewConfirmTools,
|
|
226
233
|
...mcpMutatingTools,
|
|
227
234
|
],
|
|
235
|
+
alwaysConfirmTools: DEFAULT_PERMISSION_POLICY.alwaysConfirmTools,
|
|
228
236
|
},
|
|
229
237
|
}
|
|
230
238
|
: {}),
|
|
@@ -556,6 +564,7 @@ export default async function (pi) {
|
|
|
556
564
|
registerCmuxBridge(pi);
|
|
557
565
|
}
|
|
558
566
|
export { makeAskYagniTool } from "./askYagniTool.js";
|
|
567
|
+
export { makeFileTicketTool, makeUpdateTicketStatusTool } from "./ticketTools.js";
|
|
559
568
|
export { makeAskAdvisorTool, registerAdviseCommand } from "./askAdvisorTool.js";
|
|
560
569
|
export { ADVISOR_TIER, DEFAULT_ADVISOR_LIMITS, decideConsult, formatAdvisorSubtotal, makeAdvisorState, } from "./advisor.js";
|
|
561
570
|
export { makeReviewBusinessMatchTool } from "./reviewTool.js";
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* YAGNI Code registers no tool_call handler today, so the interactive session has
|
|
5
5
|
* no plan/approval surface. P3 adds one on pi's documented `tool_call` block seam
|
|
6
6
|
* plus a `/mode` command:
|
|
7
|
-
* - auto (default):
|
|
7
|
+
* - auto (default): ordinary coding tools run directly; external tracker
|
|
8
|
+
* changes still require fresh human confirmation.
|
|
8
9
|
* - plan : blocks write/edit/bash so the agent can explore + propose without
|
|
9
10
|
* touching the tree.
|
|
10
11
|
* - review : surfaces a three-way ctx.ui.select before a write/edit/bash; a
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
* adds a session-scoped bless rule AND drafts a decision capture.
|
|
15
16
|
*
|
|
16
17
|
* `decideGate` is PURE; the live wiring holds the mode in a small closure (no
|
|
17
|
-
* module-global state). The default auto mode remains
|
|
18
|
+
* module-global state). The default auto mode remains direct for coding tools, but stricter
|
|
18
19
|
* modes fail closed for side-effect tools if the gate itself errors. Bless rules
|
|
19
20
|
* are session-scoped, path-prefix-bound, never persisted, and never consulted in
|
|
20
21
|
* plan mode (plan blocks outright before isBlessed is reached).
|
|
@@ -34,6 +35,8 @@ export interface PermissionPolicy {
|
|
|
34
35
|
planBlockTools: string[];
|
|
35
36
|
/** Tools that prompt for confirmation in review mode. */
|
|
36
37
|
reviewConfirmTools: string[];
|
|
38
|
+
/** Consequential external writes that require fresh consent in every mode. */
|
|
39
|
+
alwaysConfirmTools?: string[];
|
|
37
40
|
/**
|
|
38
41
|
* Optional: a recorded decision already blesses this action, so it auto-runs in
|
|
39
42
|
* review mode instead of prompting. The hook for tying the gate to captured
|
|
@@ -50,8 +53,8 @@ export interface GateDecision {
|
|
|
50
53
|
confirm?: boolean;
|
|
51
54
|
}
|
|
52
55
|
/**
|
|
53
|
-
* Pure permission decision for one tool call under a mode + policy.
|
|
54
|
-
*
|
|
56
|
+
* Pure permission decision for one tool call under a mode + policy. Auto allows
|
|
57
|
+
* ordinary tools; plan blocks the write/exec set; review marks writes for confirmation
|
|
55
58
|
* unless a recorded decision blesses them.
|
|
56
59
|
*/
|
|
57
60
|
export declare function decideGate(toolName: string, params: Record<string, unknown>, mode: PermissionMode, policy: PermissionPolicy): GateDecision;
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* YAGNI Code registers no tool_call handler today, so the interactive session has
|
|
5
5
|
* no plan/approval surface. P3 adds one on pi's documented `tool_call` block seam
|
|
6
6
|
* plus a `/mode` command:
|
|
7
|
-
* - auto (default):
|
|
7
|
+
* - auto (default): ordinary coding tools run directly; external tracker
|
|
8
|
+
* changes still require fresh human confirmation.
|
|
8
9
|
* - plan : blocks write/edit/bash so the agent can explore + propose without
|
|
9
10
|
* touching the tree.
|
|
10
11
|
* - review : surfaces a three-way ctx.ui.select before a write/edit/bash; a
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
* adds a session-scoped bless rule AND drafts a decision capture.
|
|
15
16
|
*
|
|
16
17
|
* `decideGate` is PURE; the live wiring holds the mode in a small closure (no
|
|
17
|
-
* module-global state). The default auto mode remains
|
|
18
|
+
* module-global state). The default auto mode remains direct for coding tools, but stricter
|
|
18
19
|
* modes fail closed for side-effect tools if the gate itself errors. Bless rules
|
|
19
20
|
* are session-scoped, path-prefix-bound, never persisted, and never consulted in
|
|
20
21
|
* plan mode (plan blocks outright before isBlessed is reached).
|
|
@@ -27,17 +28,16 @@
|
|
|
27
28
|
*/
|
|
28
29
|
import { makeBlessStore as defaultMakeBlessStore } from "./bless.js";
|
|
29
30
|
export const DEFAULT_PERMISSION_POLICY = {
|
|
30
|
-
planBlockTools: ["write", "edit", "bash"],
|
|
31
|
-
reviewConfirmTools: ["write", "edit", "bash"],
|
|
31
|
+
planBlockTools: ["write", "edit", "bash", "file_ticket", "update_ticket_status"],
|
|
32
|
+
reviewConfirmTools: ["write", "edit", "bash", "file_ticket", "update_ticket_status"],
|
|
33
|
+
alwaysConfirmTools: ["file_ticket", "update_ticket_status"],
|
|
32
34
|
};
|
|
33
35
|
/**
|
|
34
|
-
* Pure permission decision for one tool call under a mode + policy.
|
|
35
|
-
*
|
|
36
|
+
* Pure permission decision for one tool call under a mode + policy. Auto allows
|
|
37
|
+
* ordinary tools; plan blocks the write/exec set; review marks writes for confirmation
|
|
36
38
|
* unless a recorded decision blesses them.
|
|
37
39
|
*/
|
|
38
40
|
export function decideGate(toolName, params, mode, policy) {
|
|
39
|
-
if (mode === "auto")
|
|
40
|
-
return { block: false };
|
|
41
41
|
if (mode === "plan") {
|
|
42
42
|
if (policy.planBlockTools.includes(toolName)) {
|
|
43
43
|
return {
|
|
@@ -47,6 +47,11 @@ export function decideGate(toolName, params, mode, policy) {
|
|
|
47
47
|
}
|
|
48
48
|
return { block: false };
|
|
49
49
|
}
|
|
50
|
+
if (policy.alwaysConfirmTools?.includes(toolName)) {
|
|
51
|
+
return { block: false, confirm: true };
|
|
52
|
+
}
|
|
53
|
+
if (mode === "auto")
|
|
54
|
+
return { block: false };
|
|
50
55
|
// review
|
|
51
56
|
if (policy.reviewConfirmTools.includes(toolName)) {
|
|
52
57
|
if (policy.isBlessed?.(toolName, params))
|
|
@@ -96,7 +101,7 @@ const MODE_STATUS = {
|
|
|
96
101
|
review: "✓ review",
|
|
97
102
|
};
|
|
98
103
|
const MODE_COPY = {
|
|
99
|
-
auto: "auto: changes apply
|
|
104
|
+
auto: "auto: coding changes apply directly; external tracker changes ask first (default).",
|
|
100
105
|
plan: "plan: write, edit, and bash are held so the agent can explore and propose only.",
|
|
101
106
|
review: "review: you confirm each write, edit, or bash command before it applies.",
|
|
102
107
|
};
|
|
@@ -104,7 +109,32 @@ function isMode(value) {
|
|
|
104
109
|
return value === "auto" || value === "plan" || value === "review";
|
|
105
110
|
}
|
|
106
111
|
function sideEffectTools(policy) {
|
|
107
|
-
return new Set([
|
|
112
|
+
return new Set([
|
|
113
|
+
...policy.planBlockTools,
|
|
114
|
+
...policy.reviewConfirmTools,
|
|
115
|
+
...(policy.alwaysConfirmTools ?? []),
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
function boundedPromptValue(value, fallback) {
|
|
119
|
+
if (typeof value !== "string")
|
|
120
|
+
return fallback;
|
|
121
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
122
|
+
if (normalized.length === 0)
|
|
123
|
+
return fallback;
|
|
124
|
+
return normalized.length <= 80 ? normalized : `${normalized.slice(0, 77)}…`;
|
|
125
|
+
}
|
|
126
|
+
function externalTrackerPrompt(toolName, input) {
|
|
127
|
+
if (toolName === "file_ticket") {
|
|
128
|
+
const title = boundedPromptValue(input.title, "Untitled ticket");
|
|
129
|
+
const target = boundedPromptValue(input.target_key, "default project/team");
|
|
130
|
+
return `File “${title}” in ${target}?`;
|
|
131
|
+
}
|
|
132
|
+
if (toolName === "update_ticket_status") {
|
|
133
|
+
const ref = boundedPromptValue(input.ref, "ticket");
|
|
134
|
+
const status = boundedPromptValue(input.status, "requested status");
|
|
135
|
+
return `Move ${ref} to ${status}?`;
|
|
136
|
+
}
|
|
137
|
+
return "Confirm external tracker change";
|
|
108
138
|
}
|
|
109
139
|
/**
|
|
110
140
|
* Wire the tool_call gate + the /mode command onto a shared mode holder. Default
|
|
@@ -121,6 +151,7 @@ export function registerPermissionGate(pi, deps = {}) {
|
|
|
121
151
|
const effectivePolicy = {
|
|
122
152
|
planBlockTools: basePolicy.planBlockTools,
|
|
123
153
|
reviewConfirmTools: basePolicy.reviewConfirmTools,
|
|
154
|
+
alwaysConfirmTools: basePolicy.alwaysConfirmTools,
|
|
124
155
|
isBlessed: basePolicy.isBlessed ?? ((tool, params) => blessStore?.isBlessed(tool, params) ?? false),
|
|
125
156
|
};
|
|
126
157
|
const sideEffects = sideEffectTools(effectivePolicy);
|
|
@@ -136,7 +167,7 @@ export function registerPermissionGate(pi, deps = {}) {
|
|
|
136
167
|
// cannot get consent for is held rather than silently auto-applied (this
|
|
137
168
|
// mirrors plan mode, which blocks regardless of UI).
|
|
138
169
|
if (!ctx?.hasUI) {
|
|
139
|
-
return { block: true, reason:
|
|
170
|
+
return { block: true, reason: `${event.toolName} held (no UI to confirm this action).` };
|
|
140
171
|
}
|
|
141
172
|
// Lazily bind the bless store to this session's cwd.
|
|
142
173
|
if (!blessStore)
|
|
@@ -144,14 +175,16 @@ export function registerPermissionGate(pi, deps = {}) {
|
|
|
144
175
|
// Three-way prompt (pi's confirm is boolean-only, so use select): Yes,
|
|
145
176
|
// Yes-and-remember (only when a path-prefix bless is meaningful), or No.
|
|
146
177
|
const dir = blessStore.describeDir(input);
|
|
147
|
-
const blessable = dir !== null;
|
|
178
|
+
const blessable = dir !== null && !effectivePolicy.alwaysConfirmTools?.includes(event.toolName);
|
|
148
179
|
const yes = "Yes";
|
|
149
180
|
const no = "No";
|
|
150
181
|
const remember = blessable
|
|
151
182
|
? `Yes, and don't ask again for ${event.toolName} in ${dir}`
|
|
152
183
|
: undefined;
|
|
153
184
|
const options = blessable ? [yes, remember, no] : [yes, no];
|
|
154
|
-
const choice = await ctx.ui.select(
|
|
185
|
+
const choice = await ctx.ui.select(effectivePolicy.alwaysConfirmTools?.includes(event.toolName)
|
|
186
|
+
? externalTrackerPrompt(event.toolName, input)
|
|
187
|
+
: "YAGNI Code review mode", options);
|
|
155
188
|
if (choice === yes)
|
|
156
189
|
return {};
|
|
157
190
|
if (blessable && choice === remember) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
/**
|
|
4
|
+
* Ticket write-back tools (spec 2026-08-09): file a ticket, move a ticket.
|
|
5
|
+
*
|
|
6
|
+
* Both are EXPLICIT user-intent actions — the guidelines pin the agent to
|
|
7
|
+
* calling them only when the developer asked. Writes execute server-side as
|
|
8
|
+
* the developer's own tracker account (per-user write identity); when no
|
|
9
|
+
* personal connection exists the backend answers 412 `connect_required` and
|
|
10
|
+
* the tool renders the one-time connect prompt instead of failing opaquely.
|
|
11
|
+
*/
|
|
12
|
+
export interface MakeTicketToolOptions {
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
getToken: () => string | undefined;
|
|
15
|
+
fetchImpl?: typeof fetch;
|
|
16
|
+
}
|
|
17
|
+
declare const fileTicketParams: Type.TObject<{
|
|
18
|
+
title: Type.TString;
|
|
19
|
+
description: Type.TOptional<Type.TString>;
|
|
20
|
+
tracker: Type.TOptional<Type.TUnion<[Type.TLiteral<"jira">, Type.TLiteral<"linear">]>>;
|
|
21
|
+
target_key: Type.TOptional<Type.TString>;
|
|
22
|
+
}>;
|
|
23
|
+
export declare function makeFileTicketTool(opts: MakeTicketToolOptions): ToolDefinition<typeof fileTicketParams, {
|
|
24
|
+
identifier?: string;
|
|
25
|
+
url?: string | null;
|
|
26
|
+
}>;
|
|
27
|
+
declare const updateTicketStatusParams: Type.TObject<{
|
|
28
|
+
ref: Type.TString;
|
|
29
|
+
status: Type.TString;
|
|
30
|
+
tracker: Type.TOptional<Type.TUnion<[Type.TLiteral<"jira">, Type.TLiteral<"linear">]>>;
|
|
31
|
+
}>;
|
|
32
|
+
export declare function makeUpdateTicketStatusTool(opts: MakeTicketToolOptions): ToolDefinition<typeof updateTicketStatusParams, {
|
|
33
|
+
identifier?: string;
|
|
34
|
+
state?: string;
|
|
35
|
+
}>;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=ticketTools.d.ts.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { friendlyFetchError, METERED_POST_FETCH_POLICY, resilientFetch, } from "./resilientFetch.js";
|
|
3
|
+
/** Render backend problem responses (412/404/422) as agent-relayable text. */
|
|
4
|
+
async function renderProblem(toolName, res) {
|
|
5
|
+
let body = null;
|
|
6
|
+
try {
|
|
7
|
+
body = (await res.clone().json());
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
body = null;
|
|
11
|
+
}
|
|
12
|
+
if (res.status === 412 && body?.error === "connect_required") {
|
|
13
|
+
return [
|
|
14
|
+
`A personal ${body.service ?? "tracker"} connection is needed to write as you.`,
|
|
15
|
+
`Connect here (one time): ${body.connect_url ?? "(ask your admin for the connect link)"}`,
|
|
16
|
+
"Then ask me again and I'll retry.",
|
|
17
|
+
].join("\n");
|
|
18
|
+
}
|
|
19
|
+
if ((res.status === 404 || res.status === 422) && body?.error) {
|
|
20
|
+
const options = body.options?.length
|
|
21
|
+
? `\nAvailable options: ${body.options.join(", ")}`
|
|
22
|
+
: "";
|
|
23
|
+
return `${body.message ?? body.error}${options}`;
|
|
24
|
+
}
|
|
25
|
+
throw new Error(await friendlyFetchError(toolName, res));
|
|
26
|
+
}
|
|
27
|
+
const fileTicketParams = Type.Object({
|
|
28
|
+
title: Type.String(),
|
|
29
|
+
description: Type.Optional(Type.String()),
|
|
30
|
+
tracker: Type.Optional(Type.Union([Type.Literal("jira"), Type.Literal("linear")])),
|
|
31
|
+
target_key: Type.Optional(Type.String()),
|
|
32
|
+
});
|
|
33
|
+
export function makeFileTicketTool(opts) {
|
|
34
|
+
return {
|
|
35
|
+
name: "file_ticket",
|
|
36
|
+
label: "File Ticket",
|
|
37
|
+
description: "File a ticket in the workspace's tracker (Jira or Linear), attributed to the developer's " +
|
|
38
|
+
"own account. ONLY call when the user explicitly asks to file/create a ticket — never " +
|
|
39
|
+
"speculatively, never as a side effect of other work. Pass target_key (Jira project key or " +
|
|
40
|
+
"Linear team key) when the workspace has more than one.",
|
|
41
|
+
promptSnippet: "file_ticket: file a ticket in the workspace tracker as the developer (explicit request only).",
|
|
42
|
+
promptGuidelines: [
|
|
43
|
+
"Call file_ticket ONLY when the user explicitly asks to file, create, or capture a ticket.",
|
|
44
|
+
"If the tool reports a connect prompt or asks for a target/tracker, relay it verbatim and wait for the user.",
|
|
45
|
+
],
|
|
46
|
+
parameters: fileTicketParams,
|
|
47
|
+
async execute(toolCallId, params, signal) {
|
|
48
|
+
const res = await resilientFetch(`${opts.baseUrl}/api/yagni-code/tickets`, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: {
|
|
51
|
+
"content-type": "application/json",
|
|
52
|
+
authorization: `Bearer ${opts.getToken() ?? ""}`,
|
|
53
|
+
},
|
|
54
|
+
body: JSON.stringify({ ...params, idempotencyKey: toolCallId }),
|
|
55
|
+
}, { fetchImpl: opts.fetchImpl, signal, policy: METERED_POST_FETCH_POLICY });
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
const text = await renderProblem("file_ticket", res);
|
|
58
|
+
return { content: [{ type: "text", text }], details: {} };
|
|
59
|
+
}
|
|
60
|
+
const data = (await res.json());
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "text",
|
|
65
|
+
text: `Filed ${data.identifier}${data.url ? `: ${data.url}` : ""}`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
details: { identifier: data.identifier, url: data.url },
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const updateTicketStatusParams = Type.Object({
|
|
74
|
+
ref: Type.String(),
|
|
75
|
+
status: Type.String(),
|
|
76
|
+
tracker: Type.Optional(Type.Union([Type.Literal("jira"), Type.Literal("linear")])),
|
|
77
|
+
});
|
|
78
|
+
export function makeUpdateTicketStatusTool(opts) {
|
|
79
|
+
return {
|
|
80
|
+
name: "update_ticket_status",
|
|
81
|
+
label: "Update Ticket Status",
|
|
82
|
+
description: "Move a ticket to a new status by name (e.g. mark YAG-123 In Progress), attributed to the " +
|
|
83
|
+
"developer's own tracker account. ONLY call on the user's explicit request — never as an " +
|
|
84
|
+
"automatic side effect of starting or finishing work.",
|
|
85
|
+
promptSnippet: "update_ticket_status: move a tracker ticket to a named status as the developer (explicit request only).",
|
|
86
|
+
promptGuidelines: [
|
|
87
|
+
"Call update_ticket_status ONLY when the user explicitly asks to move/mark a ticket's status.",
|
|
88
|
+
"If the requested status is not available, relay the offered options and let the user pick.",
|
|
89
|
+
],
|
|
90
|
+
parameters: updateTicketStatusParams,
|
|
91
|
+
async execute(toolCallId, params, signal) {
|
|
92
|
+
const res = await resilientFetch(`${opts.baseUrl}/api/yagni-code/tickets/transition`, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
headers: {
|
|
95
|
+
"content-type": "application/json",
|
|
96
|
+
authorization: `Bearer ${opts.getToken() ?? ""}`,
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify({ ...params, idempotencyKey: toolCallId }),
|
|
99
|
+
}, { fetchImpl: opts.fetchImpl, signal, policy: METERED_POST_FETCH_POLICY });
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
const text = await renderProblem("update_ticket_status", res);
|
|
102
|
+
return { content: [{ type: "text", text }], details: {} };
|
|
103
|
+
}
|
|
104
|
+
const data = (await res.json());
|
|
105
|
+
return {
|
|
106
|
+
content: [
|
|
107
|
+
{
|
|
108
|
+
type: "text",
|
|
109
|
+
text: `${data.identifier} → ${data.state ?? params.status}`,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
details: { identifier: data.identifier, state: data.state },
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=ticketTools.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "0.2.1-staging.
|
|
3
|
+
"version": "0.2.1-staging.1038.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)",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"@earendil-works/pi-tui": "0.83.0",
|
|
39
39
|
"typebox": "^1.1.38"
|
|
40
40
|
},
|
|
41
|
-
"yagniSourceSha": "
|
|
41
|
+
"yagniSourceSha": "4493e421b21ba86ed37645beecef7c9a74799f4b"
|
|
42
42
|
}
|