fix: preserve multiple positional invocation arguments and update API schema
This commit is contained in:
+475
-5
@@ -1075,6 +1075,70 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"CompletionContext": {
|
||||||
|
"properties": {
|
||||||
|
"argument": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"invocation": {
|
||||||
|
"$ref": "#/components/schemas/FeatureInvocationIdentity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"invocation"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"CompletionEntry": {
|
||||||
|
"description": "One completion candidate for a prefix query.",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"invocation": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/FeatureInvocationDescriptor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"is_dir": {
|
||||||
|
"default": false,
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"usage": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"CompletionKind": {
|
||||||
|
"description": "Kind of completion requested by `Method::ListCompletions`.\n\n`File` is the existing `@` path lane. `Feature` lists installed chat\ninvocations. `FeatureArgument` resolves a declared argument using the\nsource-qualified [`CompletionContext`].",
|
||||||
|
"enum": [
|
||||||
|
"file",
|
||||||
|
"feature",
|
||||||
|
"feature_argument"
|
||||||
|
],
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"ConfigCommitRequest": {
|
"ConfigCommitRequest": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Compare-and-swap request for applying a complete set of source-tree changes.",
|
"description": "Compare-and-swap request for applying a complete set of source-tree changes.",
|
||||||
@@ -2232,6 +2296,98 @@
|
|||||||
},
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"FeatureInvocation": {
|
||||||
|
"properties": {
|
||||||
|
"arguments": {
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/InvocationArgumentValue"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"identity": {
|
||||||
|
"$ref": "#/components/schemas/FeatureInvocationIdentity"
|
||||||
|
},
|
||||||
|
"invocation_id": {
|
||||||
|
"description": "Stable client-generated id retained across queueing and transport retry.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "Selected public name, retained for exact draft/history restoration. Host\nresolution is always by `identity`.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"invocation_id",
|
||||||
|
"identity",
|
||||||
|
"name",
|
||||||
|
"arguments"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"FeatureInvocationDescriptor": {
|
||||||
|
"properties": {
|
||||||
|
"aliases": {
|
||||||
|
"default": [],
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"arguments": {
|
||||||
|
"default": [],
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/InvocationArgumentDescriptor"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"client_adapter": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/InvocationClientAdapter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"display_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"identity": {
|
||||||
|
"$ref": "#/components/schemas/FeatureInvocationIdentity"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"syntax": {
|
||||||
|
"$ref": "#/components/schemas/FeatureInvocationSyntax"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"identity",
|
||||||
|
"name",
|
||||||
|
"display_name",
|
||||||
|
"description",
|
||||||
|
"syntax"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"FeatureInvocationIdentity": {
|
||||||
|
"description": "Source-qualified identity of one invocation contribution, for example\n`builtin:attachments/attach`. Display names and aliases are never authority.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"FeatureInvocationSyntax": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"const": "parenthesized",
|
||||||
|
"description": "`/name(value, key=value)` with JSON-style quoted strings and `)` as the\nexplicit boundary before following natural language.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"FindingSeverity": {
|
"FindingSeverity": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"blocker",
|
"blocker",
|
||||||
@@ -2609,6 +2765,285 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"InvocationArgumentDescriptor": {
|
||||||
|
"properties": {
|
||||||
|
"completion": {
|
||||||
|
"$ref": "#/components/schemas/InvocationCompletion",
|
||||||
|
"default": {
|
||||||
|
"kind": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"description": "Zero-based positional index. `None` means named-only. A positional\nargument may also be supplied by name; this makes chip editing stable.",
|
||||||
|
"format": "uint16",
|
||||||
|
"maximum": 65535,
|
||||||
|
"minimum": 0,
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"required": {
|
||||||
|
"default": false,
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"value_type": {
|
||||||
|
"$ref": "#/components/schemas/InvocationArgumentType"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"value_type"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"InvocationArgumentType": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "string",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "integer",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "boolean",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "enum",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"values"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A Worker-readable path. This does not grant filesystem authority.",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "worker_file",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A resource selected and staged by the client. It must never be resolved\nas a path on the Worker host.",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "client_file",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"InvocationArgumentValue": {
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"$ref": "#/components/schemas/InvocationValue"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"InvocationClientAdapter": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"const": "attachment",
|
||||||
|
"description": "The client stages a local file and replaces the draft invocation with an\n`UploadedFile` chip. Local paths are not sent to or persisted by Worker.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"InvocationCompletion": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "none",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "static",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"values"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "worker_file",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "client_file",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Host-owned provider selected by source-qualified id. Metadata lookup is\nside-effect free; implementations never cross this wire boundary.",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "provider",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"provider"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"InvocationValue": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "string",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "integer",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"format": "int32",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "boolean",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"LegacyMemoryResetRequest": {
|
"LegacyMemoryResetRequest": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Explicit destructive reset request. The confirmation must exactly equal\n`RESET LEGACY MEMORY <workspace_id>` for the Workspace named in the route.",
|
"description": "Explicit destructive reset request. The confirmation must exactly equal\n`RESET LEGACY MEMORY <workspace_id>` for the Workspace named in the route.",
|
||||||
@@ -7356,7 +7791,19 @@
|
|||||||
"RuntimeWorkerCompletionsRequest": {
|
"RuntimeWorkerCompletionsRequest": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"kind": true,
|
"context": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/CompletionContext"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"$ref": "#/components/schemas/CompletionKind"
|
||||||
|
},
|
||||||
"prefix": {
|
"prefix": {
|
||||||
"default": "",
|
"default": "",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -7378,10 +7825,14 @@
|
|||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
"entries": {
|
"entries": {
|
||||||
"items": true,
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/CompletionEntry"
|
||||||
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
"kind": true,
|
"kind": {
|
||||||
|
"$ref": "#/components/schemas/CompletionKind"
|
||||||
|
},
|
||||||
"prefix": {
|
"prefix": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -7415,7 +7866,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"segments": {
|
"segments": {
|
||||||
"items": true,
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Segment"
|
||||||
|
},
|
||||||
"type": [
|
"type": [
|
||||||
"array",
|
"array",
|
||||||
"null"
|
"null"
|
||||||
@@ -8262,6 +8715,23 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Explicitly selected, source-qualified Feature invocation. The host never\npromotes free text to this variant and validates it against the installed\ndeclaration before execution.",
|
||||||
|
"properties": {
|
||||||
|
"invocation": {
|
||||||
|
"$ref": "#/components/schemas/FeatureInvocation"
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"const": "feature_invoke",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"invocation"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Source-qualified request for the host to start a Flow before committing\nthis user input. Runtime resolves it through Workspace authority and\nreplaces it with the entered state's instructions before Worker-side\ninput resolution.",
|
"description": "Source-qualified request for the host to start a Flow before committing\nthis user input. Runtime resolves it through Workspace authority and\nreplaces it with the entered state's instructions before Worker-side\ninput resolution.",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -14794,7 +15264,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Yoi Server API",
|
"title": "Yoi Server API",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"x-yoi-source-digest": "sha256:f9f0f8918dbcce386e94d5368fa938f02565ca98f9f67f07537bd88ed8c49870"
|
"x-yoi-source-digest": "sha256:0dc47aa72f9cace6e0026fbf5d9385f51b6d79c8f6a70a04954067a35e01c887"
|
||||||
},
|
},
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|||||||
@@ -65,6 +65,27 @@ Deno.test("selected invocation parsing preserves Unicode, spaces, slashes, prose
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("multiple positional arguments advance once per value independent of descriptor order", () => {
|
||||||
|
const positional: FeatureInvocationDescriptor = {
|
||||||
|
...descriptor,
|
||||||
|
arguments: [
|
||||||
|
{ name: "enabled", position: 2, required: true, value_type: { kind: "boolean" }, completion: { kind: "none" } },
|
||||||
|
{ name: "mode", position: 0, required: true, value_type: { kind: "enum", values: ["brief", "full"] }, completion: { kind: "none" } },
|
||||||
|
{ name: "limit", position: 1, required: true, value_type: { kind: "integer" }, completion: { kind: "none" } },
|
||||||
|
{ name: "note", required: false, value_type: { kind: "string" }, completion: { kind: "none" } },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
for (const input of ['/run("brief", 2, true) following prose', '/run("brief", note="資料/a b", 2, true) following prose']) {
|
||||||
|
const parsed = selectedFeatureInvocationRanges(input, [positional])[0];
|
||||||
|
assertEquals(input.slice(parsed.end), " following prose");
|
||||||
|
assertEquals(parsed.invocation.arguments.filter((argument) => argument.name !== "note"), [
|
||||||
|
{ name: "mode", value: { kind: "string", value: "brief" } },
|
||||||
|
{ name: "limit", value: { kind: "integer", value: 2 } },
|
||||||
|
{ name: "enabled", value: { kind: "boolean", value: true } },
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("unselected text, URLs, paths, and paste content are never promoted", () => {
|
Deno.test("unselected text, URLs, paths, and paste content are never promoted", () => {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
finalizeSelectedFeatureInvocations(
|
finalizeSelectedFeatureInvocations(
|
||||||
|
|||||||
@@ -278,7 +278,8 @@ function parseFeatureInvocation(
|
|||||||
while (/\s/.test(text[cursor] ?? "")) cursor++;
|
while (/\s/.test(text[cursor] ?? "")) cursor++;
|
||||||
} else {
|
} else {
|
||||||
cursor = itemStart;
|
cursor = itemStart;
|
||||||
argument = descriptor.arguments.find((candidate) => candidate.position === position++);
|
argument = descriptor.arguments.find((candidate) => candidate.position === position);
|
||||||
|
position++;
|
||||||
if (!argument) throw new Error("Too many positional Feature arguments.");
|
if (!argument) throw new Error("Too many positional Feature arguments.");
|
||||||
}
|
}
|
||||||
const parsed = parseRawValue(text, cursor);
|
const parsed = parseRawValue(text, cursor);
|
||||||
|
|||||||
Reference in New Issue
Block a user